Stevens Miller Stevens Miller - 7 days ago 8
Java Question

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.

Answer

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.

Comments