我正在审查一个名为“Effects of Type Erasure and Bridge Methods”的Java泛型Oracle路径,我无法说服自己给出解释。好奇,我在本地测试了代码,我甚至无法重现跟踪解释的行为。以下是相关代码:
public class Node<T> {
public T data;
public Node(T data) { this.data = data; }
public void setData(T data) {
System.out.println("Node.setData");
this.data = data;
}
}
public class MyNode extends Node<Integer> {
public MyNode(Integer data) { super(data); }
public void setData(Integer data) {
System.out.println("MyNode.setData");
super.setData(data);
}
}
Oracle跟踪声明此代码段的以下行为:
MyNode mn = new MyNode(5);
Node n = mn; // A raw type - compiler throws an unchecked warning
n.setData("Hello");
Integer x = mn.data; // Causes a ClassCastException to be thrown.
此类型代码段在类型擦除后应如下所示:
MyNode mn = new MyNode(5);
Node n = (MyNode)mn; // A raw type - compiler throws an unchecked warning
n.setData("Hello");
Integer x = (String)mn.data; // Causes a ClassCastException to be thrown.
我不明白这里使用的演员表或行为。当我尝试使用IntelliJ和Java 7在本地运行此代码时,我遇到了这种情况:
MyNode mn = new MyNode(5);
Node n = mn; // A raw type - compiler throws an unchecked warning
n.setData("Hello"); // Causes a ClassCastException to be thrown.
Integer x = mn.data;
换句话说,JVM不允许String
与setData()
一起使用。这对我来说实际上是直观的,它同意我对泛型的理解。由于MyNode
mn
是使用Integer
构建的,因此编译器应使用setData()
转发对Integer
的每次调用,以确保类型安全(即Integer
正在传递)。
有人可以了解Oracle踪迹中这个明显的错误吗?
答案 0 :(得分:3)
您误读了Oracle页面。如果你一直读到最后,你会发现它说明你所描述的是什么。
这不是一个写得很好的页面;作者说&#34; blah发生&#34;当他们的意思是&#34;如果是这样的情况那么发生,但我们认为这不是案例&#34;。他们的语言太松散了。
页面 - 桥梁方法 - 的重点是解释当你预测的行为(基于泛型设计+实现)是他们所建议的#34;时,你所观察到的真实行为是怎样的。一开始。
答案 1 :(得分:2)
嗯,这是在路上解释的。
在理论中,当编译类Node
时,其基类型T
将被删除为Object
。
所以实际上,它被编译成类似
的东西class Node {
public Object data;
public Node(Object data) {this.data = data; }
public void setData(Object data) {
System.out.println("Node.setData");
this.data = data;
}
}
然后创建一个子类MyNode
,它有自己的setData(Integer data)
。就Java而言,这是setData
方法的重载,而不是它的覆盖。每个MyNode
对象都有两个setData
方法。一个是setData(Object)
,它继承自Node
,另一个是setData(Integer)
。
所以基本上,如果你使用原始类型,并且用任何不是setData
的引用调用Integer
,Java对它的正常解释就是调用重载setData(Object)
这不会导致分配问题,因为data
被声明为Object
,而不是Integer
。只有当您尝试将数据分配回Integer
引用时,问题才出现。 Java的这种简单行为会导致MyNode
对象被不适当的数据“污染”。
然而,正如小道所说,编译器添加了一个“桥接”方法,使子类的行为更像您直观地思考它的方式。它向setData(Object)
添加了MyNode
的覆盖,因此您无法调用原始的,不安全的Node.setData(Object)
。在此覆盖桥方法中,有一个显式强制转换为Integer
,可确保您无法为data
分配非整数引用。
这是您在实际编译和运行示例时看到的行为。
如果您在javap -p
文件上运行MyNode.class
,您会发现它确实有两种setData
方法:
class MyNode extends Node<java.lang.Integer> {
public MyNode(java.lang.Integer);
public void setData(java.lang.Integer);
public void setData(java.lang.Object);
}