为什么创造 3 (/4) 是可能的?
Creation 3 是用于推断类型的“钻石”语法。所以new A<>
和写作一样new A<String>
.
因此我们可以用 Java 1.7 之前的长语法来替换它。
A<String> a1 = new A<String>("str");
这将通过以下构造函数,其中T
was java.lang.String
and t
is a java.lang.String
。请注意,在运行时只有一份此代码的副本,其原始类型为T
being java.lang.Object
.
public A(T t) {
a = (A<T>) new B();
// ^^^^^^ NB: This will at least give a rawtype warning
// as it causes heap pollution.
a.t = t;
}
的类型a
is A<T>
在编译时和java.lang.Object
删除了。两者的类型a.t
and t
is T
在编译时和java.lang.Object
删除了。因此它会在没有警告的情况下进行编译,并且在运行时无需进行强制转换检查。分配成功。
Creation 4 使用原始类型。你的编译器应该给出警告(使用-Xlint
)。忽略这些警告可能会导致ClassCastException
稍后的。泛型是真实虚拟机上的编译器“虚构”,它本质上遵循 Java 1.0 语言的规则。
为什么 B 对象能够在其通用整数变量 t 中存储字符串?
被擦除的类型t
in A
(因此B
) is java.lang.Object
因此,如果没有检查或检查不正确,可以存储任何引用类型。
为什么调用 5 在运行时失败?
物体位于a2.a.t
is a java.lang.Integer
. a2.a
具有静态类型A<java.lang.String>
所以静态类型a2.a.t
is java.lang.String
. javac
将插入一个强制转换以检查返回的原始类型是否符合静态类型。在这种特殊情况下,IIRC,旧版本javac
会错误地省略演员并使用println(java.lang.Object)
过载而不是println(java.lang.String)
.
您可以使用javap -c -private
看看到底在哪里checkcast
已放置说明。
结论
确保您已打开警告。任何默认情况下没有打开它们的东西都有些可疑。不要忽略或抑制警告,尤其是有关原始类型的警告。