我调查一般行为
我注意到了:
public class Hohol1 {
public class My<T> {
public <E> void test(Collection<E> es) { System.out.println("Collection<E>");
}
public void test(List<Integer> integerList) {
System.out.println("List<Integer>");
for (Integer integer : integerList) {
System.out.println(integer);
}
}
}
public static void main(String[] args) {
My my1 = new Hohol1().new My();
my1.test(new ArrayList<String>() { {add("1");} });
}
}
上面的代码返回
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
at GenericsTest.Hohol1$My.test(Hohol1.java:22)
at GenericsTest.Hohol1.main(Hohol1.java:31)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
但是,如果我My
非通用,那么
public class Hohol1 {
public class My/*deleted generic type T*/ {
....
}
}
此代码返回Collection<E>
这对我来说是一种惊喜的行为,我不明白为什么会这样做。
你怎么看?
P.S。我对简洁
使用双括号初始化答案 0 :(得分:3)
首先,当您使用内部类My
的原始形式时,类中的所有内容,包括不相关的指定类型参数(如方法参数List<Integer> integerList
)好像他们自己的类型擦除已经到位,例如List integerList
。
但是,您传递了ArrayList
的{{1}}(实际上只是ArrayList
)的匿名子类String
。最具体的方法是test(List<Integer> integerList)
,因为它被视为List
,ArrayList
肯定是for
。在尝试打印Integer
的增强型String
循环之前,所有内容仍会一直运行,但无法将"1"
转换为Integer
到ClassCastException
,所以你得到My
。
但是如果你使integerList
非泛型,那么参数List<Integer>
的类型不会被删除。然后,编译器可以看到Collection<E>
不匹配,但test(Collection<E> es)
匹配,因此"Collection<E>"
匹配并打印{{1}}。
原因在JLS, Section 4.8:
中指定更准确地说,原始类型被定义为以下之一:
- 通过获取泛型类型声明的名称而不带伴随类型参数列表而形成的引用类型。
...
和
未从其超类或超接口继承的原始类型C的构造函数(第8.8节),实例方法(第8.4节,第9.4节)或非静态字段(第8.3节)M的类型是原始的在与C对应的泛型声明中对应于其类型擦除的类型。
还给出了推理:
原始类型的使用仅允许作为遗留代码兼容性的让步。在将泛型引入Java编程语言之后编写的代码中使用原始类型是非常不鼓励的。未来版本的Java编程语言可能会禁止使用原始类型。