【问题标题】:Casting Class instance of an generic class using wildcards使用通配符转换泛型类的类实例
【发布时间】:2022-01-18 12:15:55
【问题描述】:

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

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&lt;A&gt; cannot be converted to Class&lt;? extends C&lt;?&gt;&gt; list.add((Class&lt;? extends C&lt;?&gt;&gt;) A.class);

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

如何将A.class 的实例添加到我的 ArrayList 中? 直觉上看来这应该是可能的。

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

起初我认为这可能是一个错误,但我在不同的 Java 版本(8 和 11)上对此进行了测试,并且问题发生在所有版本上。此外,这种类型的演员表似乎太“简单”了,因为这可能会在多个版本中被忽视,所以希望这可以通过设计来完成。

【问题讨论】:

  • 我在 intellij java: incompatible types 中遇到编译错误。你在做什么似乎有点奇怪 - 创建接口 B,创建由 B 的子类参数化的接口 D(因此它由扩展 B 的接口参数化,但它不是 B 本身的子类)然后你创建 2 个由接口的子类参数化的静态类(顺便说一句:info)?这非常令人困惑,您要实现什么目标?可能有一个非常简单的解决方案,不需要这种过度工程。记住亲吻:D
  • Eclipse 4.22 为最终的 list.add 提供了“未经检查的演员表”警告,所以它并不是真的很开心。
  • static List&lt;Class&lt;? extends C&gt;&gt; list = new ArrayList&lt;&gt;(); 这个列表的定义可以正常工作。这对你有用吗?任何其他信息都会有所帮助,这是一个非常有趣的挑战。
  • @ferrouskid 静态只是为了使最小的示例可以运行。可能只是忽略了静态,因为我的实际代码无论如何都没有它们。我更新了代码,使其不需要静态。 > 这太令人困惑了,你想达到什么目的?我知道这个例子在这个最小的形式下看起来很傻,但这是生产代码的减少,它将它扩展到多个类和数千行代码。我刚刚创建了这个以将问题减少到易于阅读的最小形式。
  • @ferrouskid 该示例来自(继承的)非常复杂的生产代码,多年来一直在使用并且肯定可以正常工作。我刚刚发现它是因为我们出于部署原因需要更改 JDK。

标签: java eclipse generics compiler-errors eclipse-jdt


【解决方案1】:

不确定您要达到的目标,但效果很好:

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&lt;?&gt;C&lt;D&lt;T&gt;&gt; 的超类,所以List&lt;? extends C&lt;?&gt;&gt; 接受C&lt;?&gt; 类型的对象及其所有子对象(@987654328 的变体@ 由任何类型 T 参数化),这就是为什么在其中粘贴 A 的实例会产生错误。

AC 的子类对List&lt;A&gt;List&lt;C&gt; 没有影响,它们唯一的关系是are children of List<?>

这就是为什么我认为我们一直遇到错误,而AC&lt;D&lt;T&gt;&gt;List&lt;A&gt;List&lt;C&gt; 没有关系。我同意如果保留这种关系会感觉很直观。

【讨论】:

  • 我知道这个例子在这个最小的形式下看起来很傻,但这是一个生产代码的减少,它将它扩展到多个类和数千行代码。我刚刚创建了这个以将问题减少到易于阅读的最小形式。替换代码很困难,因为团队中没有人了解它的业务逻辑(我知道这也不是很好)。
  • 我明白了,破解别人的密码总是很困难的。如果没有更多上下文,很难说出在这里尝试做什么。当我尝试运行您的原始代码时,我仍然收到incompatible types 编译错误,但这意味着生产代码没有像我们希望的那样工作,或者您可能稍微不正确地减少了问题?
  • 只是为了澄清。编译或运行代码时遇到类型不兼容的错误?除了为什么 Eclipse JDT 可以编译这个而 Oracle JDK 不能编译这个问题之外,我试图理解为什么这两种类型不兼容。 A 从字面上扩展了 C 与一个作为泛型类型的类,它不应该配合,因为转换的 > 不强制任何边界。
  • 虽然您的代码解决了示例,但在我的用例中无法将 static List&lt;Class&lt;? extends C&lt;?&gt;&gt;&gt; list = new ArrayList&lt;&gt;(); 更改为 static List&lt;Class&lt;? extends C&gt;&gt; list = new ArrayList&lt;&gt;();,因为以后使用此属性需要未绑定的 C 并且不能使用原始类型C. 我不明白的是为什么未绑定的C&lt;?&gt; 似乎与C&lt;D&lt;T&gt;&gt; 不兼容。
  • 另一件有点奇怪的事情是@SuppressWarnings("unchecked") Class&lt;? extends C&lt;?&gt;&gt; cc = (Class&lt;? extends C&lt;?&gt;&gt;) new A().getClass(); list.add(cc); 显然在工作。
【解决方案2】:

我认为@ferrouskid 指出这不起作用的原因可能是,尽管普遍认为,C&lt;?&gt; 不是C&lt;D&lt;?&gt;&gt; 的超类型。

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多