该问题也可以看作:为什么在打印时为String的泛型分配了Integer的值?但是,该问题的答案似乎很明显,标题问题对我而言却不是。我不明白为什么一个错误而不是另一个错误。问题适用于此示例。假设您有一个参数化的Box类:
class Box <T> {
T value;
void setValue(T value) {
this.value = value;
}
T getValue() {
return value;
}
}
并且您有该类的三个实例,一个实例没有类型参数,另外两个实例具有String或Integer。字符串和整数框被实例化为原始对象:
Box rawBox = new Box();
Box <Integer> intBox = rawBox;
Box <String> stringBox = rawBox;
[当我们通过原始引用传递setValue()值,并通过各个参数化引用传递getValue()时,出现了我遇到的问题:
Integer integerVal = 4;
rawBox.setValue(integerVal);
System.out.println(intBox.getValue());
// System.out.println(stringBox.getValue()); // ClassCastException a Integer cannot be assigned to a String
rawBox.setValue("hi");
System.out.println(intBox.getValue()); // Why is there no ClassCastException for assigning a String to Integer?
System.out.println(stringBox.getValue());
ClassCastException错误仅在打印getValue()时发出,而不在调用setValue()时发出。因此,为什么实例化为原始类型的Integer类型参数的参数化对象可以通过原始引用为其通用类型分配String值,并且在打印getValue()时却没有运行时错误,但是如果相同的参数化类型具有String的类型参数,并且它的通用类型通过原始类型分配了Integer的值,当它的getValue()打印时,它将引发ClassCastException?
您可能理解,Box.value
在运行时是Object
,因此Box.getValue
必须返回Object
。
对于所有原始类型,PrintStream.println
和Object
,String
都有重载。据推测,这是为了避免在已知要打印的值为Object.toString
时额外调用String
。
因此,想象一下两次调用println
时生成的字节码是什么样的:
Integer integerVal = 4;
rawBox.setValue(integerVal);
System.out.println(intBox.getValue());
在这种情况下,intBox.getValue()
返回Object
,因此我们将调用接受println
的Object
版本。编译器知道返回值应该为Integer
,但这没关系,因为没有println
的重载可以接受Integer
。
System.out.println(stringBox.getValue());
这里,stringBox.getValue()
返回Object
时,编译器知道它应该是String
,并希望调用接受println
的String
版本。这需要将返回值向下转换为String
,该操作失败,因为它实际上是Integer
。