在Java中,在泛型类型的实例上调用getClass时如何避免原始类型?

Jea*_*let 8 java generics reflection language-design bounded-wildcard

假设我在Java中有这个:

List<String> list = new ArrayList<String>();
list.getClass();
Run Code Online (Sandbox Code Playgroud)

最后一个表达式的类型是Class<? extends List>.我理解为什么,由于擦除,它不可能Class<? extends List<String>>.但为什么不能呢Class<? extends List<?>>

如果我想将这个表达式的结果分配给一个变量,以某种方式保存这个类实际上是某种类型的信息,我是否无法避免未经检查的强制转换警告原始类型警告List

Class<? extends List> listClass = list.getClass(); // raw type warning
Class<? extends List<?>> listClass = (Class<? extends List<?>>) list.getClass(); // unchecked cast warning
Run Code Online (Sandbox Code Playgroud)

Pau*_*ora 6

当首次引入泛型,getClass返回时Class<? extends X>,X调用它的表达式的静态类型在哪里.这种行为导致了不合理的编译问题,正如此Oracle错误中所报告的那样.这是bug报告的例子:

以下程序片段无法编译

void f(List<Integer> li, List<String> ls) {
    if (li.getClass() == ls.getClass())
  ;
}
Run Code Online (Sandbox Code Playgroud)

由于交叉点Class<List<Integer>>Class<List<String>>是空的.

通过扩大现在的返回类型getClass来解决此问题.从文档:

实际结果的类型是Class<? extends |X|>其中|X|是静态类型上其表达的擦除getClass被调用.

这解决了上述问题,但因此导致了您的问题指出的问题.不久之后,报告了另一个错误,争论如下:

我认为getClass()输入规则可以更改为Class<? extends wildcard(T)>

通配符操作定义如下:if Tis parametrized, wildcard(T)=erasure(T)<?>else, wildcard(T)=T

理由:

  1. 此规则引入原始类型.原始类型必须仅用于与遗留代码交互.

  2. 新规则引入了通配符.参数化类型和通配符之间的关系基于子类型规则.参数化类型和通配符之间的关系基于原始类型转换.

这个错误没有采取行动,至今仍未解决,并提出以下抗议:

该提议意味着getClass()将返回一个Class<? extends ArrayList<?>>与其他Class<? extends ArrayList<?>>对象不兼容的对象.这与现有代码兼容,如:

List<String> l = ...;
Class<? extends List> c = l.getClass();
Run Code Online (Sandbox Code Playgroud)

因为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);
Run Code Online (Sandbox Code Playgroud)

并获得警告cast(),因为未经检查的转换ListList<Integer>.但是根据提案,类似的代码不能编译:

List<Integer> x = ...;
Class<? extends List<?>> cl = x.getClass();
List<Integer> y = cl.cast(null);
Run Code Online (Sandbox Code Playgroud)

因为List<?>返回者cast()无法转换为List<Integer>.避免错误的唯一方法是投 cl.cast(..)List和遭受选中转换警告 List<Integer>.这实际上getClass()已经是这样了.

总体而言,该提案似乎是一个好主意,但它具有中等复杂性和相当小的收益.

(删节并纠正了一些拼写错误)