【问题标题】:Java compiler not able to infer type on generic chainJava 编译器无法推断泛型链上的类型
【发布时间】:2020-01-13 11:18:36
【问题描述】:

如下所述(我可能对自己的解释很糟糕):

我想了解这个问题背后的原理,以便将这些知识应用到真正的问题中。


问题开始

我正在一个系统中工作,该系统旨在成为一个可供许多子系统使用的抽象库。这个想法是有一个可以通过实现扩展的标准行为。 我的问题是 java 编译器无法推断方法参数类型,即使它没有任何意义,因为边界在每个相关的泛型类/方法上都设置得很好。

下面有一个例子,是重现问题的最小例子。我知道在这个例子中看起来有点傻,但那是因为简化了:

    public class Test {
    public static void main(String[] args) {
        Gen<? extends Base> gen = new HijaGen();
        gen.applyTo(Hija.EXAMPLE);
    }
    private interface Base {
        String getName();
    }
    private enum Hija implements Base {
        EXAMPLE;
        @Override
        public String getName() {
            return this.name();
        }
    }
    private interface Gen<T extends Base> {
        boolean applyTo(T base);
    }
    private static class HijaGen implements Gen<Hija> {
        @Override
        public boolean applyTo(Hija base) {
            return false;
        }
    }
}

基本上它说 applyTo 期望 Base 和 Hija 的子类是无效的,在我看来这没有任何意义。

提前致谢。

编辑:

此代码用于库,因此解决方案无法指定类型,此后无法通过特定实现进行扩展。

我已经知道,如果我指定泛型类型而不是抛出类型通配符,它​​将完美地工作。但我的问题是,即使 Hija 是 Base 的子类,而且方法公司需要 Base 的子类,它怎么可能永远不会编译......

我想了解这个问题背后的原理,以便我可以将这些知识应用到真正的问题中。

【问题讨论】:

  • 声明“此代码用于库”并没有真正的帮助。了解应该如何访问库很重要。您提供的 main 方法是一个示例调用,但不是您期望需要库的方法。客户在使用 lib 时会关心哪些变量类型是相关问题。
  • @daniu 我认为你专注于一些不重要的事情。具体问题是:即使 Hija 满足(至少在理论上)公司要求的方法,这种方法怎么可能不接受 Hija?关于图书馆的提及只是为了澄清为什么我不能只使用 Gen 这是快速解决方案
  • 好吧,您标记为已接受的答案包含我的评论所要表达的内容,即可能需要更改对库的调用。

标签: java generics type-inference generic-programming


【解决方案1】:

Gen&lt;? extends Base&gt;somethingGen,它扩展了 Base。它适用于一些特定类型的Base,但我们不知道是哪一种。实际上,这意味着您将永远无法在类型为 Gen&lt;? extends Base&gt; 的变量上调用 applyTo,除非传递 null。

将您的代码更改为

Gen<Hija> gen = new HijaGen();
gen.applyTo(Hija.EXAMPLE);

我怀疑你可能会说你不能这样做,因为你的代码只是一个例子,上面的代码在真实的代码中是不可能的。在这种情况下,您需要举一个更好的例子

【讨论】:

  • Gen&lt;? super Hija&gt;
  • @TomHawtin-tackline 或只是HijaGen
  • 我认为 OP 追求的是一个一般原则,而不是仅仅获取一些随机代码来编译。
  • @TomHawtin-tackline 见第一段
  • 您好,谢谢大家,我将尝试进一步澄清:此代码用于库,因此该解决方案无法指定类型,此后无法通过特定实现进行扩展。我已经知道,如果我指定泛型类型而不是抛出类型通配符,它​​将完美地工作。但我的问题是,即使 Hija 是 Base 的子类,而且方法公司需要 Base 的子类,它怎么可能永远不会编译......我想了解这个问题背后的原理,以便我可以将这些知识应用于真正的问题.
【解决方案2】:

我认为文档here 中解释了一个类似的问题,他们提出的问题是制作一个帮助器(或包装器)来澄清类型。所以我想你可以尝试添加这个功能:

private static <T extends Base> boolean applyTo(Gen<T> gen, T base){
    return gen.applyTo(base);
}

并将main函数改成如下:

public static void main(String[] args) {
    boolean b = applyTo(new HijaGen(), Hija.EXAMPLE);
}

【讨论】:

    【解决方案3】:

    根据 Carlos 提到的内容,还有另一种方法可以解决此问题。如果您确定Gen&lt;T&gt;的实施供应商,则可以使用它。

    在这里,我们定义了一个工厂方法来创建Gen 实现的实例并将其转换为Gen&lt;Base&gt;,而不是调用applyTo() 的助手,我认为这对于所有实际目的都是安全的。请注意,工厂方法get() 不必是静态的。其余代码与您的示例保持不变。

    public static void main( String[] args ){
        Gen<Base> gen = (Gen<Base>) get();
        gen.applyTo( Hija.EXAMPLE );
    }
    
    static Gen<? extends Base> get(){
        /* Create the instance of the Gen interface implementation here. */
        return new HijaGen();
    }
    

    说明

    Gen&lt;? extends Base&gt; 期望引用Gen 的实现实例,该实例使用Base 或其子类型的类型。 由于“子类型”可以来自许多可能的层次结构之一Base向下(如下图所示),因此不能确定传递给applyTo()方法的参数是具有相同的子层次结构路径,而不仅仅是具有相同祖先父 Base 的“亲戚”。这就是为什么它不允许调用applyTo(),其引用为Gen&lt;? extends Base&gt;,参数为Base

    但是,当引用为Gen&lt;Base&gt; 时,它知道applyTo() 将接受任何属于Base 子类型的参数。因此,它不再担心类型不匹配。

    我试图说明不同子层次结构路径的含义。这就像一棵家谱。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-08-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-22
      相关资源
      最近更新 更多