Вы на самом деле уже ответили на свой вопрос: стирание - это первая граница или объект, если тип не ограничен. Давайте рассмотрим несколько примеров:
public class A implements Serializable {
// since the first bound is Cloneable, the erasure is Cloneable
static <T extends Cloneable & Serializable> void doSomething1(T t) {}
// since the first bound is Serializable, the erasure is Serializable
static <T extends Serializable & Cloneable> void doSomething2(T t) {}
// since T is not bounded, the erasure is Object
static <T> void doSomething(T t) {
System.out.println("A");
}
// the type of A is a class, so it has to come first
// the erasure is A since it comes first
static <T extends A & Serializable> void doSomething(T t) {
System.out.println("B");
}
// not possible because the bounds are two classes
// static <T extends Object & A> void doSomething(T t) {return null;}
}
Поскольку стираемые данные разные, методы могут иметь одно и то же имя! Но это совершенно не рекомендуется и довольно запутанно, поскольку поведение меняется:
public static void main(String[] args) {
A.doSomething(new Object()); // prints "A"
A.doSomething(new A()); // prints "B"
}
Чтобы ответить на ваш вопрос после редактирования: Нет, он не является лишним. Указание типа, который должен быть реализован параметром типа, дает вам доступ к методам границ. Давайте рассмотрим следующий пример:
public final class Box<T extends A & Comparable<T>> {
private T item1;
private T item2;
int compare() {
// this is the compare method of Comparable interface
return item1.compareTo(item2);
}
}
Из приведенного выше примера вы видите, что A
не реализует интерфейс Comparable
. Это означает, что если вы просто напишите Box<T extends A>
, вы не сможете сравнить два элемента в вашем Box
, используя метод compareTo
просто потому, что A
не реализует Comparable
. Если вы хотите, чтобы элементы вашего ящика имели класс A
и для реализации интерфейса Comparable
, вам нужно указать обе границы.
Это не ссылка A
, которая реализуетComparable
но ссылка T
! Даже если стирание T extends A & Comparable<T>
будет A
, компилятор может выполнять приведение на более низком уровне. И это то, что здесь происходит. Это можно увидеть, если проверить байт-код с помощью утилиты javap
, где используется инструкция checkcast :
int compare();
....
0: aload_0
1: getfield #7 // Field item1:LA;
4: checkcast #13 // class java/lang/Comparable
7: aload_0
8: getfield #15 // Field item2:LA;
11: invokeinterface #18, 2 // InterfaceMethod java/lang/Comparable.compareTo:(Ljava/lang/Object;)I
16: ireturn
....