Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does type inference fail for lambda, but succeed for equivalent method reference?

I am using a lambda to implement a functional interface in the Java program below. When the lambda is passed as an argument to a generic method, the compiler flags an "incompatible types" error because it infers that the lambda implements the Func<Shape> interface, which has the compiler interpreting the lambda parameter ("thing") as being of type Shape when the lambda attempts to pass it to a method (testRound) that requires an argument of type Round. That error makes sense to me.

But the equivalent method reference does not provoke an error message. I had been under the misconception that a lambda and a method reference that could replace that lambda were interchangeable. Here, that's not so.

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
{
}

Why does the method reference succeed when the lambda fails?

UPDATE

Vince Emigh found the answer, which I've marked as accepted, below. While it's not part of my question, here are four ways to work around the fact that the lambda is only inferred as being of type Func<Shape> if one were really stuck on using lambdas:

// 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);

I don't see any reason to prefer one of these over the method reference, unless one feels that lambdas are a little less dense (and, maybe, a little more readable). But, they're there if you want them.

like image 716
Stevens Miller Avatar asked Nov 26 '16 01:11

Stevens Miller


People also ask

Why method reference is better than lambda?

The method references can only be used to replace a single method of the lambda expression. A code is more clear and short if one uses a lambda expression rather than using an anonymous class and one can use method reference rather than using a single function lambda expression to achieve the same.

Does it makes sense to replace lambda expression with method references?

If you have been coding in Java 8 then you may know that using method reference in place of lambda expression makes your code more readable, hence it is advised to replace lambda expression with method reference wherever possible.

How target type is inferred for the lambda expression?

The target type of the lambda expressions is inferred from the context, which is an assignment statement to a Callable<String> reference variable. Subsequently, the reference variable is used to invoke the call() method.

How lambda expression works in Java?

Lambda Expressions were added in Java 8. A lambda expression is a short block of code which takes in parameters and returns a value. Lambda expressions are similar to methods, but they do not need a name and they can be implemented right in the body of a method.


1 Answers

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.

like image 166
Vince Avatar answered Sep 28 '22 00:09

Vince