为什么此具有泛型的代码在Java 11中引发ClassCastException?

时间:2019-01-23 19:44:59

标签: java generics

下面的代码在Java 8中成功,但是在Java 11中引发了ClassCastException。为什么行为发生了变化?

我在OpenJDK的Java 9,Java 10或Java 11功能集中找不到任何相关更改。

public class GenericsExample {

    public static void main(String[] args) {
        Set<Car> set = new HashSet<>();
        set.add(getAnimal());
    }

    static <T extends Animal> T getAnimal() {
        return (T) new Animal() {};
    }

    interface Animal {}

    class Car {}
}

1 个答案:

答案 0 :(得分:6)

实际上,它是Java 8中的bug,在Java 9-bugfix中已得到修复。 在某些情况下,跳过了javac CHECKCAST指令。

如果您感到好奇,请考虑这两行代码:

Set<Car> set = new HashSet<>(); // line 11
set.add(getAnimal());           // line 12

Java 8字节码如下所示:

 LINENUMBER 11 L1
 ALOAD 1
 ALOAD 0
 INVOKEVIRTUAL UserManagerTest.getAnimal ()LUserManagerTest$Animal;
 INVOKEINTERFACE java/util/Set.add (Ljava/lang/Object;)Z (itf)
 POP

但是Java 9看起来像这样:

LINENUMBER 11 L1
ALOAD 1
ALOAD 0
INVOKEVIRTUAL UserManagerTest.getAnimal ()LUserManagerTest$Animal;
CHECKCAST UserManagerTest$Car
INVOKEINTERFACE java/util/Set.add (Ljava/lang/Object;)Z (itf)
POP

唯一的区别是CHECKCAST指令,(根据JavaDoc)指出已解析的命名类,数组或接口类型。如果可以将object强制转换为已解析的类,数组或接口类型,则操作数堆栈不变。否则,checkcast指令将引发ClassCastException