使用通配符转换通用 class 的 Class 实例

Casting Class instance of an generic class using wildcards

我目前真的无法理解这里发生的事情。我有以下(最小)代码:

public class SillyCast {

    interface B {}
    interface D<T extends B> {}
    class C<T extends D<?>> {}
    class A<T extends B> extends C<D<T>> {}
    List<Class<? extends C<?>>> list = new ArrayList<>();

    void m() {
        list.add((Class<? extends C<?>>) A.class);
    }
}

在 Eclipse 中编译(和 运行)这段代码时,一切都按预期工作,但是当从命令行(Oracle JDK)执行时,我在 期间收到以下错误编译:

error: incompatible types: Class<A> cannot be converted to Class<? extends C<?>> list.add((Class<? extends C<?>>) A.class);

我知道 Oracle JDK 的行为并不总是与 Eclipse JDT 完全相同,但这看起来很奇怪。

如何将 A.class 的实例添加到我的 ArrayList 中? 直觉上这似乎是可行的。

为什么 Oracle JDK 和 Eclipse JDT 对此转换有不同的看法?为什么 Oracle JDK 在执行此转换时遇到问题。

起初我认为这可能是一个错误,但我在不同的 Java 版本(8 和 11)上测试了这个问题,所有版本都出现了这个问题。此外,这种演员表似乎太“简单”了,因为这可能会在多个版本中被忽视,所以希望通过设计做到这一点。

不确定您要实现的目标,但这很好用:

public class Main{
    static interface B {}
    static interface D<T extends B> {}
    static class C<T extends D<?>> {}
    static class A<T extends B> extends C<D<T>>{}

    static List<Class<? extends C>> list = new ArrayList<>();
    public static void main(String[] args) {
        list.add(A.class);
        list.add(C.class);
    }
}

这真是令人困惑,你想达到什么目的?您正在通过扩展其他接口的接口进行参数化(然后通过 类 扩展由扩展接口的接口参数化的 类 )。这种复杂程度表明它可能需要一种不同的、更简单的方法 :D

编辑:这对你有用吗? (编译运行没有错误):

public static void main(String[] args) {
    List<Class<? super A<?>>> list = new ArrayList<>();
    list.add(A.class);
    list.add(C.class);
}

我可能错了,这有点令人困惑,但据我了解 C<?>C<D<T>> 的超类,因此 List<? extends C<?>> 接受类型为 [=12= 的对象] 及其所有子项(C 的变体由任何类型 T 参数化),这就是为什么在其中粘贴 A 的实例会出错。

A 作为 C 的子类对 List<A>List<C> 没有影响,它们唯一的关系是它们 are children of List<?>.

这就是为什么我认为我们一直运行出错,而A是一个C<D<T>>List<A>List<C>没有关系。我同意,如果保留这种关系,感觉会很直观。

我认为正如@ferrouskid 所指出的,这不起作用的原因可能是,尽管人们普遍认为,C<?> 不是 C<D<?>> 的超类型。

尽管这与其说是真正的解决方案,不如说是一种混淆策略,但我目前的解决方法是通过在转换之前将 A.class 分配给变量来分散 Oracle 编译器的注意力(可能需要 @SuppressWarnings("unchecked"))

Class<?> aa = A.class;
list.add((Class<? extends C<?>>) aa);

免责声明:这种操作非常危险,因为转换可能会在运行时失败。因此,仅当您对受影响的方法进行了完整的测试覆盖时才执行此操作。