如何擦除泛型 "replace" 多个边界
How erasure of generics "replace" multiple bounds
我读到,在类型擦除过程中,Java 编译器擦除所有类型参数,如果类型参数是有界的,则用第一个边界替换每个参数,如果类型参数是无界的,则用 Object 替换。
但是,我无法理解,指定类型参数需要实现的接口是不是多余的。例如:
public class Box<T extends Something,Seralizable,Cloneable>
如果erasure把classBox
里面的T
替换成Something
(Class引用),那不就意味着interfaces:Seralizable,Cloneable
必须由 Something class 实现,所以只有我觉得指定 Seralizable,Cloneable
接口是多余的?此外,如果在菱形中仅提及接口会发生什么,默认情况下 T
是否视为 Object
参考?
我很乐意为泛型 Class 提供一个示例,为泛型方法提供一个示例(如果泛型方法中存在多个扩展)。
你实际上已经回答了你的问题:擦除是第一个边界或对象,如果类型没有边界的话。让我们看几个例子:
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>
你不能使用 compareTo
方法比较 Box
中的两个项目,因为 A
没有实现 Comparable
.如果你想让你的box item是class A
和来实现Comparable
接口,你需要指定这两个bounds.
实现Comparable
的不是A
引用,而是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
....
我读到,在类型擦除过程中,Java 编译器擦除所有类型参数,如果类型参数是有界的,则用第一个边界替换每个参数,如果类型参数是无界的,则用 Object 替换。 但是,我无法理解,指定类型参数需要实现的接口是不是多余的。例如:
public class Box<T extends Something,Seralizable,Cloneable>
如果erasure把classBox
里面的T
替换成Something
(Class引用),那不就意味着interfaces:Seralizable,Cloneable
必须由 Something class 实现,所以只有我觉得指定 Seralizable,Cloneable
接口是多余的?此外,如果在菱形中仅提及接口会发生什么,默认情况下 T
是否视为 Object
参考?
我很乐意为泛型 Class 提供一个示例,为泛型方法提供一个示例(如果泛型方法中存在多个扩展)。
你实际上已经回答了你的问题:擦除是第一个边界或对象,如果类型没有边界的话。让我们看几个例子:
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>
你不能使用 compareTo
方法比较 Box
中的两个项目,因为 A
没有实现 Comparable
.如果你想让你的box item是class A
和来实现Comparable
接口,你需要指定这两个bounds.
实现Comparable
的不是A
引用,而是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
....