use*_*er7 7 java generics type-erasure generic-collections
public class Box<T> {
private T element;
public T getElement() {
return element;
}
public void setElement(T element) {
this.element = element;
}
}
public class Test {
public static void main(String[] args) {
List<Box> l = new ArrayList<>(); //Just List of Box with no specific type
Box<String> box1 = new Box<>();
box1.setElement("aa");
Box<Integer> box2 = new Box<>();
box2.setElement(10);
l.add(box1);
l.add(box2);
//Case 1
Box<Integer> b1 = l.get(0);
System.out.println(b1.getElement()); //why no error
//Case 2
Box<String> b2 = l.get(1);
System.out.println(b2.getElement()); //throws ClassCastException
}
}
Run Code Online (Sandbox Code Playgroud)
该列表l包含类型的元素Box.在第一种情况下,我获得第一个元素Box<Integer>,在第二种情况下,列表中的第二个元素获得为Box<String>.在第一种情况下不抛出ClassCastException.
当我试图调试中,element's在类型b1和b2是String和Integer分别.
它与类型擦除有关吗?
准确来说,问题是PrintStream#println。
让我们使用以下命令检查编译后的代码javap -c Test.class:
72: invokevirtual #12 // Method blub/Box.getElement:()Ljava/lang/Object;\n75: invokevirtual #13 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V\nRun Code Online (Sandbox Code Playgroud)\n正如您所看到的,编译器删除了类型,并且还省略了 的强制转换Integer,因为这里没有必要。编译器已将使用的重载方法链接到PrintStream#(Object)。\n由于JLS 规则 \xc2\xa75.3 ,它会这样做:
\n\n方法调用转换应用于方法或构造函数调用中的每个参数值(\xc2\xa78.8.7.1、\xc2\xa715.9、\xc2\xa715.12):参数表达式的类型必须转换为对应参数的类型。
\n方法调用上下文允许使用以下之一:
\n\n
\n- 身份转换(\xc2\xa75.1.1)
\n- 扩大原始转换(\xc2\xa75.1.2)
\n- 扩大引用转换 ( \xc2\xa75.1.5 )
\n- 装箱转换 ( \xc2\xa75.1.7 ) 可选地后跟扩大引用转换
\n- 拆箱转换 ( \xc2\xa75.1.8 ) 可选地随后进行扩展基元转换。
\n
第三条规则是从子类型到超类型的转换:
\n\n\n存在从任何引用类型 S 到任何引用类型 T 的扩大引用转换,前提是 S 是 T 的子类型 ( \xc2\xa74.10 )。
\n
并且在检查类型是否可以拆箱之前完成(第五次检查:“拆箱转换”)。因此编译器会检查它Integer是 的子类型Object,因此它必须调用#println(Object)(如果您检查被调用的重载版本,您的 IDE 会告诉您相同的信息)。
另一方面,第二个版本:
\n 95: invokevirtual #12 // Method blub/Box.getElement:()Ljava/lang/Object;\n 98: checkcast #14 // class java/lang/String\n101: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;)V\nRun Code Online (Sandbox Code Playgroud)\n有一个checkcast检查检索到的类型Box#getElement是否确实是 a String。这是必要的,因为您告诉编译器它将是 a String(由于泛型类型Box<String> b2 = l.get(1);)并且它链接了 method PrintStream#(String)。此检查因上述内容而失败ClassCastException,因为Integer无法将其强制转换为String.
| 归档时间: |
|
| 查看次数: |
593 次 |
| 最近记录: |