Oracle';关于Java泛型的研究
我当时正在查看Oracle关于Java泛型的一条线索,标题为“”,我无法说服自己给出了一个解释。出于好奇,我在本地测试了代码,甚至无法重现trail解释的行为。以下是相关代码:Oracle';关于Java泛型的研究,java,generics,type-erasure,Java,Generics,Type Erasure,我当时正在查看Oracle关于Java泛型的一条线索,标题为“”,我无法说服自己给出了一个解释。出于好奇,我在本地测试了代码,甚至无法重现trail解释的行为。以下是相关代码: public class Node<T> { public T data; public Node(T data) { this.data = data; } public void setData(T data) { System.out.println("Nod
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);
}
}
删除类型后,此代码段应如下所示:
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将不允许将字符串
用于setData()
。这对我来说是很直观的,它与我对泛型的理解是一致的。由于MyNode
mn
是使用Integer
构造的,因此编译器应该使用Integer
强制执行对setData()
的每次调用,以确保类型安全(即,正在传入一个Integer
)
有人能解释一下Oracle跟踪中这个明显的错误吗?您误读了Oracle页面。如果你一直读到最后,你会发现它说发生的事情就是你所描述的 这一页写得不好;作者说“胡说八道”,他们的意思是“如果是这样的话,那么胡说八道就会发生,但我们会看到事实并非如此”。他们的语言太松散了
页面桥接方法的要点是解释当预测行为(基于泛型acutal设计+实现)是他们在开始时“建议”的时候,你观察到的真实行为是怎样的。好的,这在试验中解释 理论上,当类
节点
被编译时,它的基本类型T
被擦除为对象
所以在现实中,它被编译成
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(整数数据)
。就Java而言,这是setData
方法的重载,而不是重写。每个MyNode
对象都有两个setData
方法。一个是setData(Object)
它继承自节点
,另一个是setData(Integer)
因此,基本上,如果您使用raw类型,并使用任何非整数的引用调用setData
,Java对此的正常解释是调用重载setData(Object)
这不会导致赋值问题,因为数据
声明为对象
,而不是整数
。只有当您尝试将数据分配回整数
引用时,才会出现问题。Java的这种简单行为会导致MyNode
对象被不适当的数据“污染”
然而,正如trail所说,编译器添加了一个“桥”方法,使子类的行为更像您直观地想象的方式。它将setData(Object)
的覆盖添加到MyNode
,因此您无法调用原始的非安全节点。setData(Object)
。在这个覆盖桥方法中,有一个显式转换为整数
,确保您不能将非整数引用分配给数据
这就是您在实际编译和运行示例时看到的行为
如果在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);
}
类MyNode扩展节点{
公共MyNode(java.lang.Integer);
公共void setData(java.lang.Integer);
公共void setData(java.lang.Object);
}
我认为,说该页面的语言过于松散,给了它太多的信任。因此,您要说的是,调用n.setData(“Hello”)
实际上是命中编译器生成的桥接方法,它将在调用超级方法之前将传递给它的每个对象转换为Integer
。这就解释了我所想的一切,谢谢你的见解。我倾向于批评糟糕的文档,但我已经习惯于发布糟糕的文档,尤其是API文档。具有讽刺意味的是,Java在年轻时拥有一些最好的文档。现在。。。我尽量克制自己的轻蔑:)。
class MyNode extends Node<java.lang.Integer> {
public MyNode(java.lang.Integer);
public void setData(java.lang.Integer);
public void setData(java.lang.Object);
}