甲骨文在Java泛型方面的一个潜在问题
我正在审查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轨迹中看到这个明显的错误吗?
您误解了Oracle页面。 如果你一直读到最后,你会发现它说明发生了什么是你描述的。
这不是一个写得很好的页面; 作者说,当他们的意思是“如果有这种情况发生了等等,但是我们认为那不是这种情况”,那么“等等发生了”。 他们的语言过于宽松。
页面 - 桥接方法 - 的要点是,当预测的行为(基于泛型实例设计+实现)是他们在开始时“建议”的时候,解释实际行为是如何观察的。
那么,在路上解释。
理论上讲,当类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
方法。 一个是从Node
继承的setData(Object)
,另一个是setData(Integer)
。
所以基本上,如果你使用原始类型,并且用任何不是Integer
引用调用setData
,Java对此的常规解释就是调用重载setData(Object)
。
这不会导致赋值问题,因为data
被声明为Object
,而不是Integer
。 只有当您尝试将数据分配回Integer
引用时,问题才会发生。 这种简单的Java行为会导致MyNode
对象被不适当的数据“污染”。
然而,正如线索所言,编译器添加了一个“桥接”方法,使得子类的行为更像您直觉地认为它的方式。 它向MyNode
添加了setData(Object)
的重写 ,这样就不能调用原始的,非安全的Node.setData(Object)
。 在这种压倒一切的电桥法,有一个显式的Integer
,以确保您将无法分配非整数参考data
。
这就是您实际编译和运行示例时看到的行为。
如果你在MyNode.class
文件上运行javap -p
,你会发现它确实有两个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);
}
链接地址: http://www.djcxy.com/p/89863.html
上一篇: Potential issue with one of Oracle's trails on Java generics
下一篇: How do I get array item type in TypeScript using the Reflection API?