为什么类型推断对于lambda失败,但成功进行等效的方法引用?

时间:2016-11-26 01:32:33

标签: java generics lambda type-inference

我正在使用lambda在下面的Java程序中实现一个功能接口。当lambda作为参数传递给泛型方法时,编译器会标记一个"不兼容的类型"错误,因为它推断lambda实现了Func< Shape> interface,当lambda尝试将其传递给需要类型为Round的参数的方法(testRound)时,编译器将lambda参数(" thing")解释为Shape类型。这个错误对我有意义。

但等效方法引用不会引发错误消息。我一直误以为lambda和可以替换lambda的方法引用是可以互换的。在这里,情况并非如此。

public class Main
{
    public static void main(String... args)
    {
        methodB(thing -> Main.testRound(thing)); // incompatible types
        methodB(Main::testRound);                // no problem here
    }

    static <T extends Shape> void methodB(Func<T> function)
    {
    }

    static boolean testRound(Round thing)
    {
        return true;
    }
}

interface Func<T>
{
    boolean test(T ob);
}

class Shape
{
}

class Round extends Shape
{
}

为什么lambda失败时方法引用会成功?

更新

Vince Emigh在下面找到了我已经接受的答案。虽然它不是我的问题的一部分,但是有四种方法可以解决这样一个事实:如果一个人真的坚持使用lambda,那么lambda只被推断为Func<Shape>类型:

// Use a type witness.

Main.<Round>methodB(thing -> testRound(thing));

// Make the lambda's argument type explicit.

methodB((Round thing) -> testRound(thing));

// Cast the argument.

methodB(thing -> testRound((Round)thing));

// Store the lambda reference in a Func<Round> variable.

Func<Round> lambda = thing -> testRound(thing);
methodB(lambda);

我没有看到任何理由更喜欢其中一个而不是方法参考,除非有人认为lambdas的密度稍低(并且可能更具可读性)。但是,如果你想要它们,他们会在那里。

1 个答案:

答案 0 :(得分:2)

From JLS §15.13.2:

Unlike a lambda expression, a method reference can be congruent with a generic function type (that is, a function type that has type parameters). This is because the lambda expression would need to be able to declare type parameters, and no syntax supports this; while for a method reference, no such declaration is necessary.

The lambda expression raises an error since there is no type argument specified. This causes T to be compiled as Shape (as mentioned in your post), since there's nothing to help infer the argument's type.

As for method references, since the type can be inferred from the method's parameters, no explicit type argument is needed, as mentioned in the JLS statement above.