我无法理解当涉及集合时类型擦除到底是如何工作的。
如果我们看一下这段代码:
static void print(Object o) {
System.out.println("Object");
}
static void print(String s) {
System.out.println("String");
}
static void print(Integer i) {
System.out.println("Integer");
}
static <T> void printWithClass (T t) {
print(t);
}
public static void main(String[] args) {
printWithClass("abc"); // Object is printed, because T turns to Object after type erasure.
}
这个例子我理解,但是这个我不能理解:
class Printer<T>{
void print(Collection<?> c){
System.out.println("1");
}
void print(List<Number> l){
System.out.println("2");
}
void printWithClass(List<T> ts){
print(ts);
}
}
public class Main
{
public static void main(String[] args){
Printer<Integer> printer=new Printer<>();
printer.printWithClass(new ArrayList< Integer>()); //1 is printed
}
}
我无法理解为什么精确地打印 1。在我看来,类型擦除后的代码将如下所示:
void print(Collection c){
System.out.println("1");
}
void print(List l){
System.out.println("2");
}
void printWithClass(List ts){
print(ts);
}
但这是错误的。
在我看来,类型擦除有两个阶段 - 第一阶段将 T 转换为 Object,第二阶段
List<...>
将转换为 List
。
我认为这是不正确的,有人可以解释一下这里发生了什么吗?
编辑:我提出了这个猜测:编译器会记住最初的参数是
List<Number>
,因为它对于单独编译很重要。但是,它不记得最初传入 printWithClass
的内容也是 List<Number>
,它只能知道它是 List。
请记住,方法重载是在编译时解决的(与方法overriding相反,它将在运行时解决)。
这意味着必须在编译 printWithClass
时决定调用哪个特定方法签名
。这也意味着此时不会进行类型擦除(因为擦除是运行时的属性,所以编译器仍然关心所有类型参数)。在此方法中:
void printWithClass(List<T> ts){
print(ts);
}
当 T
完全无界(即它可以是任何可能的引用类型)时,只有一种可能的
print
方法可以调用,即采用
Collection<?>
的方法,因为我们无法知道
T
是
Number
。顺便说一句,由于
List<Integer>
不是
List<Number>
,即使这些规则不同,您的
main
方法也不会导致调用其他
print
方法。