当第一次引入泛型时,getClass 返回Class<? extends X>,其中X 是调用它的表达式的静态类型。如this Oracle bug 中所述,此行为导致了不合理的编译问题。这是该错误报告的示例:
以下程序片段编译失败
void f(List<Integer> li, List<String> ls) {
if (li.getClass() == ls.getClass())
;
}
因为Class<List<Integer>> 和
Class<List<String>> 为空。
通过将getClass 的返回类型扩大到现在的样子,这个问题得到了解决。来自the documentation:
实际结果类型为Class<? extends |X|>,其中|X| 是调用getClass 的表达式的静态类型的擦除。
这解决了上述问题,但因此导致了您的问题指出的问题。不久之后,another bug 被举报,争论如下:
我认为getClass()的打字规则可以改为Class<? extends
wildcard(T)>
通配符操作定义为:如果T被参数化,
wildcard(T)=erasure(T)<?> 否则,
wildcard(T)=T
理由:
此规则引入了原始类型。原始类型只能用于与遗留代码交互。
新规则引入了通配符。参数化类型和通配符之间的关系基于子类型化规则。参数化类型和通配符之间的关系是基于原始类型转换的。
这个错误没有被采取行动,直到今天仍然存在,有以下反驳意见:
该提议意味着getClass() 将返回一个Class<? extends
ArrayList<?>> 对象,该对象与其他Class<? extends
ArrayList<?>> 对象不兼容。这与现有代码兼容,例如:
List<String> l = ...;
Class<? extends List> c = l.getClass();
因为 RHS 的新类型 Class<? extends List<?>> 是
Class<? extends List>.
丰富 Class 的类型参数的一个缺点是它会
打破Class.cast 的惯用用法。今天,你可以写:
List<Integer> x = ...;
Class<? extends List> cl = x.getClass();
List<Integer> y = cl.cast(null);
并在cast() 收到警告,因为从List 到List<Integer> 的转换未经检查。但是对于该提案,类似的代码无法编译:
List<Integer> x = ...;
Class<? extends List<?>> cl = x.getClass();
List<Integer> y = cl.cast(null);
因为cast()返回的List<?>无法转换
到List<Integer>。避免错误的唯一方法是强制转换
cl.cast(..) 到 List 并遭受未经检查的转换警告
List<Integer>。这实际上是 getClass() 已经做的事情。
总体而言,该提案似乎是一个好主意,但它有适度的
复杂性和相当小的回报。
(已删节,部分错别字已更正)