【问题标题】:Why can't javac infer generic type arguments for functions used as arguments?为什么 javac 不能为用作参数的函数推断泛型类型参数?
【发布时间】:2013-02-02 01:57:41
【问题描述】:

在下面的示例中,为什么编译器能够推断出Foo.test() 中对Foo.create() 的第一次调用的通用参数,但在第二次调用中却不能这样做?我正在使用 Java 6。

public class Nonsense {
    public static class Bar {
        private static void func(Foo<String> arg) { }
    }

    public static class Foo<T> {

        public static <T> Foo<T> create() {
            return new Foo<T>();
        }

        private static void test() {
            Foo<String> foo2 = Foo.create(); // compiles
            Bar.func(Foo.create());          // won't compile
            Bar.func(Foo.<String>create());  // fixes the prev line
        }
    }
}

(编译错误是类型Nonsense.Bar中的方法func(Nonsense.Foo)不适用于参数(Nonsense.Foo))。

注意:我知道编译器错误可以通过 test() 中的第三行来修复 - 我很好奇是否存在阻止编译器推断类型的特定限制。 在我看来这里有足够的上下文。

【问题讨论】:

  • 我不知道你期待什么答案,除了“它不够聪明。”
  • @Louis - 可以想象,不够聪明是不可能的,但我还没弄清楚为什么。
  • @bacar:可能足够聪明,但事实并非如此。

标签: java generics type-inference


【解决方案1】:

从 Java 7 开始,必须先进行方法重载解析,然后才能考虑您正在调用的方法中的任何目标类型信息,以尝试在 func 的声明中推断类型变量 T。这似乎很愚蠢,因为我们都可以看到,在这种情况下,只有一个名为 func 的方法,但是,它是 JLS 规定的,并且是 Java 7 中 javac 的行为。

编译过程如下:首先,编译器看到它正在编译对名为 func 的类 Bar 的静态方法的调用。要执行重载决议,它必须找出调用该方法的参数。尽管这是一个微不足道的案例,但它仍然必须这样做,并且在它这样做之前,它没有任何关于可用于帮助它的方法的形式参数的信息。实际参数由一个参数组成,即对Foo.create() 的调用,它被声明为返回Foo&lt;T&gt;。同样,在目标方法没有条件的情况下,它只能推断出返回类型是 Foo&lt;T&gt; 的擦除,即 Foo&lt;Object&gt;,它确实如此。

然后方法重载解析失败,因为func 的任何重载都不与Foo&lt;Object&gt; 的实际参数兼容,因此会发出错误。

这当然是非常不幸的,因为我们都可以看到,如果信息可以简单地从另一个方向流动,从方法调用的目标返回到调用站点,则可以很容易地推断出类型,并且不会有错误。事实上,Java 8 中的编译器可以做到这一点,而且确实做到了。正如另一个答案所述,这种更丰富的类型推断对于 Java 8 中添加的 lambda 以及为利用 lambda 进行的 Java API 扩展非常有用。

您可以从上述链接下载Java 8 with JSR 335 lambdas 的预发布版本。它编译问题中的代码,没有任何警告或错误。

【讨论】:

    【解决方案2】:

    从上下文推断类型太复杂了。主要障碍可能是方法重载。比如f(g(x)),要确定申请哪个f(),我们需要知道g(x)的类型;但是g(x) 的类型可能需要从f() 的参数类型中推断出来。在某些语言中,简单地禁止方法重载,以便更容易进行类型推断。

    在 Java 8 中,您的示例可以编译。由于 lambda 表达式用例,Java 团队更有动力去扩展类型推断。这不是一件容易的事。

    Java 7 的 Java 语言规范包含 40 页,仅用于规范方法调用表达式(第 15.12 节)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-11
      相关资源
      最近更新 更多