我不明白编译器在输出 Test 时如何处理下面的代码,而我预计会出现错误。
List<Integer> b = new ArrayList<Integer>();
List a = b;
a.add("test");
System.out.println(b.get(0));
我希望有人能告诉我编译器在执行代码时所经历的确切步骤,以便我能够理解输出。我目前的理解是:
- 编译器会在编译期间检查 List 类中是否存在支持参数类型的 add 方法,该类的原始类型是 add(Object e)。
- 但是,在运行时,它会尝试从实际对象 List
调用 add(Object e) ,因为实际对象不是原始类型,而是持有此方法add(Integer e) 方法。
如果在实际对象 List
最佳答案
你很接近。编译时间检查所有结果:
a
类型为 List
所以调用
a.add("test");
成功了。 b
是(编译时)类型 ArrayList<Integer>
所以
b.get(0)
也 checkout 。请注意,检查仅针对变量的编译时类型。当编译器看到 a.add("test")
它不知道变量 a
引用的对象的运行时值.一般来说,它确实不能(理论计算机科学对此有一个结果),尽管控制流类型分析可以捕获许多这样的东西。像 TypeScript 这样的语言可以在编译时做出惊人的事情。
现在您可能会假设在运行时可以检查这些事情。 las,在 Java 中他们不能。 Java 删除泛型类型。查找有关 Java 类型删除的文章以了解详细信息。 TL;DR 是一个 List<Integer>
在编译时变为原始 List
在运行时。 JVM 没有办法“具体化”泛型(尽管其他语言可以!)所以当引入泛型时,Java 决定只删除泛型类型。所以在运行时,你的代码没有类型问题。
让我们看一下编译后的代码:
0: new #2 // class java/util/ArrayList
3: dup
4: invokespecial #3 // Method java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: astore_2
10: aload_2
11: ldc #4 // String test
13: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
18: pop
19: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
22: aload_1
23: iconst_0
24: invokeinterface #7, 2 // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
29: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
32: return
这里可以直接看到没有运行时类型检查。因此,对您的问题的完整(但看似轻率)的回答是,Java 仅在编译时根据变量(在编译时已知)的类型检查类型,但泛型类型参数被删除并代码在没有它们的情况下运行。
关于java - 意外地将字符串添加到 List<Integers>,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52681096/