首页 > 解决方案 > 为什么 Integer 的泛型在打印时没有分配 String 的值发出错误?

问题描述

这个问题也可以看成:为什么一个泛型的 String 赋值为 Integer 在打印时会发出运行时错误?但是这个问题的答案似乎很明显,标题问题对我来说不是。我不明白为什么一个错误而不是另一个错误。这个问题适用于这个例子。假设您有一个参数化的 Box 类:

class Box <T> {
    T value;

    void setValue(T value) {
        this.value = value;
    }

    T getValue() {
        return value;
    }
}

你有这个类的三个实例,一个没有类型参数,另外两个有字符串或整数。String 和 Integer 的 Box 被实例化为原始对象:

    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 类型参数的参数化对象实例化为 rawtype 可以通过原始引用为其泛型分配 String 值,并且在打印 getValue() 时没有运行时错误,但如果相同的参数化类型有String 的类型参数和它的泛型通过原始类型分配了 Integer 的值,它会在打印 getValue() 时抛出 ClassCastException?

标签: javagenericsclasscastexception

解决方案


您可能明白这Box.valueObject在运行时,因此Box.getValue必须返回Object

PrintStream.println对于所有基本类型、 forObject和 for都有重载StringObject.toString这大概是为了避免在打印的值已知为 a 时额外调用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,编译器知道它应该是 aString并且想要调用println接受 a的版本String。这需要将返回值向下转换为String,这会失败,因为它实际上是一个Integer.


推荐阅读