java javac 为什么不能为用作参数的函数推断泛型类型参数?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14651589/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 16:58:29  来源:igfitidea点击:

Why can't javac infer generic type arguments for functions used as arguments?

javagenericstype-inference

提问by bacar

In the following sample, why is the compiler able to infer the generic arguments for the first call to Foo.create()in Foo.test(), but not able to do so in the second? I'm using Java 6.

在下面的示例中,为什么编译器能够为第一次调用Foo.create()in推断通用参数Foo.test(),但不能在第二次调用时这样做?我正在使用 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
        }
    }
}

(The compile error is The method func(Nonsense.Foo) in the type Nonsense.Bar is not applicable for the arguments (Nonsense.Foo)).

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

Note: I understand the compiler error can be fixed by the third line in test() - I'm curious as to whether there is a specific limitation that prevents the compiler from being able to infer the type. It appearsto me that there is enough context for it here.

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

回答by David Conrad

As of Java 7, method overload resolution has to proceed before any target type information from the method you are calling can be taken into account to try to infer the type variable Tin the declaration of func. It seems foolish, since we can all see that in this case there is one and only one method named func, however, it is mandated by the JLS and is the behavior of javacfrom Java 7.

从Java 7中,方法重载具有您所呼叫可以考虑尝试推断类型变量的方法的任何目标类型信息之前进行T的申报func。这似乎很愚蠢,因为我们都可以看到,在这种情况下,只有一个名为 的方法func,但是,它是 JLS 强制要求的,并且是javacJava 7 中的行为。

Compilation proceeds as follows: First, the compiler sees that it is compiling a call to a static method of class Bar named func. To perform overload resolution, it must find out what parameters the method is being called with. Despite this being a trivial case, it must still do so, and until it has done so it does not have any information about the formal parameters of the method available to help it. The actual parameters consist of one argument, a call to Foo.create()which is declared as returning Foo<T>. Again, with no criteria from the target method, it can only deduce that the return type is the erasure of Foo<T>which is Foo<Object>, and it does so.

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

Method overload resolution then fails, since none of the overloads of funcis compatible with an actual parameter of Foo<Object>, and an error is emitted to that effect.

然后方法重载解析失败,因为 的重载func都与 的实际参数不兼容Foo<Object>,并且会发出一个错误以达到该效果。

This is of course highly unfortunate since we can all see that if the information could simply flow in the other direction, from the target of the method call back towards the call site, the type could readily be inferred and there would be no error. And in fact the compiler in Java 8 can do just that, and does. As another answer stated, this richer type inferencing is very useful to the lambdas that are being added in Java 8, and to the extensions to the Java APIs that are being made to take advantage of lambdas.

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

You can download a pre-release build of Java 8 with JSR 335 lambdasfrom the preceding link. It compiles the code in the question without any warnings or errors.

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

回答by irreputable

Inferring types from context is too complicated. The main obstacle is probably method overloading. For example, f(g(x)), to determine which f()to apply, we need to know the type of g(x); yet the type of g(x)may need to be inferred from f()'s parameter types. In some languages method overloading is simply prohibited so that type inference can be easier.

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

In Java 8 your example compiles. The Java team is more motivated to broaden type inference due to lambda expression use cases. It's not an easy task.

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

The java language specification for java 7 contains 40 pages just to spec method invocation expression (section 15.12)

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