【问题标题】:In Java, how can I avoid raw types when calling getClass on an instance of a generic type?在 Java 中,如何在泛型类型的实例上调用 getClass 时避免原始类型?
【发布时间】:2013-09-13 09:31:10
【问题描述】:

假设我在 Java 中有这个:

List<String> list = new ArrayList<String>();
list.getClass();

最后一个表达式的类型是Class&lt;? extends List&gt;。我明白为什么由于擦除,它不能是Class&lt;? extends List&lt;String&gt;&gt;。但是为什么不能是Class&lt;? extends List&lt;?&gt;&gt;呢?

如果我想将此表达式的结果分配给以某种方式保持这个类实际上是某种List的信息?

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

【问题讨论】:

标签: java generics reflection language-design bounded-wildcard


【解决方案1】:

当第一次引入泛型时,getClass 返回Class&lt;? extends X&gt;,其中X 是调用它的表达式的静态类型。如this Oracle bug 中所述,此行为导致了不合理的编译问题。这是该错误报告的示例:

以下程序片段编译失败

void f(List<Integer> li, List<String> ls) {
    if (li.getClass() == ls.getClass())
  ;
}

因为Class&lt;List&lt;Integer&gt;&gt;Class&lt;List&lt;String&gt;&gt; 为空。

通过将getClass 的返回类型扩大到现在的样子,这个问题得到了解决。来自the documentation

实际结果类型为Class&lt;? extends |X|&gt;,其中|X| 是调用getClass 的表达式的静态类型的擦除。

这解决了上述问题,但因此导致了您的问题指出的问题。不久之后,another bug 被举报,争论如下:

我认为getClass()的打字规则可以改为Class<? extends wildcard(T)>

通配符操作定义为:如果T被参数化, wildcard(T)=erasure(T)&lt;?&gt; 否则, wildcard(T)=T

理由:

  1. 此规则引入了原始类型。原始类型只能用于与遗留代码交互。

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

这个错误没有被采取行动,直到今天仍然存在,有以下反驳意见:

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

List<String> l = ...;
Class<? extends List> c = l.getClass();

因为 RHS 的新类型 Class&lt;? extends List&lt;?&gt;&gt;Class&lt;? extends List&gt;.

丰富 Class 的类型参数的一个缺点是它会 打破Class.cast 的惯用用法。今天,你可以写:

List<Integer> x = ...;
Class<? extends List> cl = x.getClass();  
List<Integer> y = cl.cast(null);

并在cast() 收到警告,因为从ListList&lt;Integer&gt; 的转换未经检查。但是对于该提案,类似的代码无法编译:

List<Integer> x = ...;
Class<? extends List<?>> cl = x.getClass();
List<Integer> y = cl.cast(null);

因为cast()返回的List&lt;?&gt;无法转换 到List&lt;Integer&gt;。避免错误的唯一方法是强制转换 cl.cast(..)List 并遭受未经检查的转换警告 List&lt;Integer&gt;。这实际上是 getClass() 已经做的事情。

总体而言,该提案似乎是一个好主意,但它有适度的 复杂性和相当小的回报。

(已删节,部分错别字已更正)

【讨论】:

    【解决方案2】:

    由于List 是一个接口,我不确定是否有可能发现List 接口是为ArrayList 实现的。找到此链接 here 可能会有所帮助。

    我确实搞砸了一点,发现...

        Class<?> d = list.getClass();
        d.equals(ArrayList.class);
    

    但我不确定这是否是您要寻找的...

    祝你好运!

    【讨论】:

    • 这与List是一个接口无关。 ArrayList 部分也无关紧要,您的示例也是如此。我对getClass() 方法的静态返回类型感兴趣。
    • 好吧,原谅我的无知,但是如果您正在寻找类型,除了 ArrayList 之外,您如何期望其他任何东西,因为它是被实例化的类型?如果您想知道 list 是否实现了 List,instanceof 不工作吗?我确实对您提出的解决方案感到非常好奇,如果您可以让我们更新。
    • 我对@9​​87654329@ 在List&lt;String&gt;(或List&lt;?&gt;)类型的实例上调用时返回的静态类型(即编译器可以知道的返回类型)感兴趣事情)。它实际上是ArrayList 的事实并不重要。
    猜你喜欢
    • 1970-01-01
    • 2015-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多