【问题标题】:java.lang.Class generics and wildcardsjava.lang.Class 泛型和通配符
【发布时间】:2015-05-07 01:09:18
【问题描述】:

为什么下面的代码不能编译?

interface Iface<T> { }

class Impl<T> implements Iface<T> { }

class TestCase {
    static Class<? extends Iface<?>> clazz = Impl.class;
}

错误是

java:不兼容的类型:java.lang.Class&lt;Impl&gt; 无法转换为java.lang.Class&lt;? extends Iface&lt;?&gt;&gt;

但我不明白为什么通配符不捕获。

【问题讨论】:

  • 另请参阅"Cannot convert from List<List> to List<List<?>>" 以获取详细说明。 (您正在尝试将Class&lt;Impl&gt; 转换为Class&lt;? extends Iface&lt;?&gt;&gt;,因此基本上适用相同的规则。)如果您真的需要这个,那么按照我在那里描述的方式进行转换应该没问题。 (Class&lt;? extends Iface&lt;?&gt;&gt;)(Class&lt;? extends Impl&gt;)Impl.class 否则,请避免使用原始类型参数。
  • @Radiodef 感谢您的链接!遗憾的是,演员表不适用于注释参数,这是激发这个问题的现实用例。
  • 哎哟。我不知道该建议什么。您可以将注释更改为Class&lt;? extends Iface&gt;,但它是嗯。取决于您使用该类的目的。你可以用这种方式对其进行注释,然后再进行 kludge 投射。

标签: java generics bounded-wildcard


【解决方案1】:

这里的子类型关系是:

          Class<? extends Iface>
           ╱                  ╲
Class<? extends Iface<?>>   Class<Impl>

(我在对'Cannot convert from List&lt;List&gt; to List&lt;List&lt;?&gt;&gt;' 的回答中解释了这一点。)

所以本质上它不会编译,因为它是横向转换。

如果可能的话,你可以做我在那边描述的演员:

(Class<? extends Iface<?>>)(Class<? extends Impl>)Impl.class

如果你不能做演员,那么你可能只需要处理一个原始的有界Class&lt;? extends Iface&gt;。这很烦人,主要是因为警告,但它打开了错误的可能性:

interface Iface<T> {
    void accept(T a);
}

class Impl2 implements Iface<String> {
    public void accept(String a) { }
}

class TestCase {
    static Class<? extends Iface> clazz = Impl2.class;

    public static void main(String[] args) throws Exception {
        // throws ClassCastException
        clazz.newInstance().accept(new Object());
    }
}

不太可能发生,但我想这取决于你在做什么。


我倾向于认为这是 Java 类型系统的问题。

  • 可能应该有一个特殊规则,类型参数? extends T&lt;?&gt; 包含类型参数? extends T,例如Class&lt;? extends T&gt; 转换为 Class&lt;? extends T&lt;?&gt;&gt;。从定义子类型的现有方式(TT&lt;?&gt; 的超类型)的角度来看,这没有意义,但从类型安全的角度来看是有意义的。

  • 或者例如List.class 应该是 Class&lt;List&lt;?&gt;&gt; 而不是 Class&lt;List&gt;

  • 或者其他比我聪明的人能想出的聪明事。

我上面描述的ClassCastException 的有趣之处在于它完全是人造的。事实上,使用 unchecked cast 来阻止它会导致警告。

我猜这只是 Java 中的泛型尚未完成的标志。

【讨论】:

    【解决方案2】:

    由于type-erasure,当您说Impl.class 时,您会得到Class&lt;Impl&gt;。也就是说,你可以说

    Class<Impl> clazz = Impl.class;
    

    泛型是一种编译时类型安全特性。

    【讨论】:

    • 但其他类似的结构,如Class&lt;? extends CharSequence&gt; clazz = String.class 工作。当我写 class Impl implements Iface&lt;String&gt; { }Class&lt;? extends Iface&gt; clazz 时它也有效。
    • @TavianBarnes 关键是String 不接受任何通用参数 - 正如您所指出的,如果Impl 没有任何通用参数,它也可以正常工作。 (请注意,这两个类 inherit 都来自具有泛型参数的其他类,这没关系 - 如果该类具有泛型参数 itself,则难以编译器(因为类型擦除。)
    • @berry120 好吧,现在对我来说很有意义。 Class&lt;Impl&gt; 中的原始类型 Impl 导致 Impl 层次结构中的所有类型都被删除,因此我们实际上有 Impl extends Iface 但没有 Impl extends Iface&lt;?&gt; 用于任何 ?。谢谢!
    猜你喜欢
    • 2011-11-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多