为什么javac不能推断用作参数的函数的泛型类型参数? [英] Why can't javac infer generic type arguments for functions used as arguments?

查看:563
本文介绍了为什么javac不能推断用作参数的函数的泛型类型参数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在下面的示例中,为什么编译器能够推断出 Foo中第一次调用 Foo.create()的泛型参数。 test(),但在第二个时间不能这样做?

  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(); //编译
Bar.func(Foo.create()); //不会编译
Bar.func(Foo。< String> create()); //修正了上一行



code> $ / pre>

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

注意:我明白编译器错误可以通过test()中的第三行修复 - 我很好奇是否存在一个特定的限制,以防止编译器能够推断出类型。对于我来说,这里有足够的上下文。

解决方案

从Java 7开始,方法重载决议必须在调用方法的任何目标类型信息之前进行,以便在<$ c的声明中试图推断类型变量 T $ C> FUNC 。这似乎很愚蠢,因为我们都可以看到,在这种情况下,只有一个名为 func 的方法,但是,它由JLS强制执行,并且是<

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



方法重载解析失败,因为没有任何重载 func Foo< Object> 的实际参数兼容,并且出现此错误。



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



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


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.

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)).

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 appears to me that there is enough context for it here.

解决方案

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 T in 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 javac from Java 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.

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

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.

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

这篇关于为什么javac不能推断用作参数的函数的泛型类型参数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆