我正在从JavaDocs阅读此示例在第一个示例中,文档说它将调用setData(Object)
方法,但是当我尝试代码时,它从不调用该方法,而是调用{{ 1}}方法。
但是当我实现这个例子时,它给出了运行时异常
n.setData的java.lang.ClassCastException(" Hello");
setData(Integer data)
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);
}
}
ClassCast异常将在第4行发生。它没有那样发生。
答案 0 :(得分:2)
在同一个教程中已经解释了所发生的事情。
n.setData("Hello");
导致方法setData(Object)
在类MyNode
的对象上执行。 (MyNode
类继承了setData(Object)
的{{1}}。)- 在
Node
的正文中,setData(Object)
引用的对象的data
字段已分配给n
。- 可以访问同一个对象的
String
字段,通过data
引用,并且应该是一个整数(因为mn
是mn
,这是一个MyNode
Node<Integer>
。- 尝试将
String
分配给Integer
会导致Java编译器在分配时插入的强制转换ClassCastException
。
所以,正如你所看到的,这就是你所经历的。在尝试将String分配给整数时,方法调用会导致异常。
令人困惑的是,代码中的注释可能是错误的。它试图告诉你代码不会到达第4行,因为这一点会引发异常。但是,如果评论在第3行,那么实际上会抛出异常,那就更清楚了。
答案 1 :(得分:1)
当使用泛型参数覆盖方法并且重写方法具有更多特定绑定时,会发生以下情况。在基本类字节码中,您可以使用带有擦除签名的方法:
Object data;
public void setData(Object data) {
System.out.println("Node.setData");
this.data = data;
}
由于子类不再是通用的,所以字节码中的方法与源中的方法完全相同:
public void setData(Integer data) {
System.out.println("MyNode.setData");
super.setData(data);
}
但是,您可以看到此方法不会覆盖超类方法,因为参数类型不同。尽管如此,它必须被覆盖。为此,java编译器在MyNode
类中生成一个特殊的合成桥方法:
public void setData(Object data) {
this.setData((Integer)data);
}
所以,你的演员阵容失败了。当你调用n.setData("Hello")
时,它是一个虚拟调用,并调用这个合成方法,当然会抛出异常。
您可能认为替代解决方案不是引入桥接方法,而是在编译期间更改MyNode
中的参数类型:
public void setData(Object data) {
System.out.println("MyNode.setData");
super.setData(data);
}
此类解决方案会引入更多问题,尤其是代码二进制兼容性问题。您MyNode
的用户不应该知道您的类扩展了某些通用参数,它是一个实现细节。他们应该能够使用正常setData
参数调用您的Integer
。您可以稍后更改超类以删除Node
中的泛型参数(假设所有节点现在都是整数),MyNode
类的用户仍然可以正常工作而无需重新编译。