Lambda Expression and generic defined only in method

JavaGenericsLambdaJava 8

Java Problem Overview


Suppose I've a generic interface:

interface MyComparable<T extends Comparable<T>>  {
    public int compare(T obj1, T obj2);
}

And a method sort:

public static <T extends Comparable<T>> 
       void sort(List<T> list, MyComparable<T> comp) {
    // sort the list
}

I can invoke this method and pass a lambda expression as argument:

List<String> list = Arrays.asList("a", "b", "c");
sort(list, (a, b) -> a.compareTo(b));

That will work fine.

But now if I make the interface non-generic, and the method generic:

interface MyComparable {
    public <T extends Comparable<T>> int compare(T obj1, T obj2);
}

public static <T extends Comparable<T>> 
       void sort(List<T> list, MyComparable comp) {
}

And then invoke this like:

List<String> list = Arrays.asList("a", "b", "c");
sort(list, (a, b) -> a.compareTo(b));

It doesn't compile. It shows error at lambda expression saying:

> "Target method is generic"

OK, when I compiled it using javac, it shows following error:

SO.java:20: error: incompatible types: cannot infer type-variable(s) T#1
        sort(list, (a, b) -> a.compareTo(b));
            ^
    (argument mismatch; invalid functional descriptor for lambda expression
      method <T#2>(T#2,T#2)int in interface MyComparable is generic)
  where T#1,T#2 are type-variables:
    T#1 extends Comparable<T#1> declared in method <T#1>sort(List<T#1>,MyComparable)
    T#2 extends Comparable<T#2> declared in method <T#2>compare(T#2,T#2)
1 error

From this error message, it seems like compiler is not able to infer the type arguments. Is that the case? If yes, then why is it happening like this?

I tried various ways, searched through the internet. Then I found this JavaCodeGeeks article, which shows a way, so I tried:

sort(list, <T extends Comparable<T>>(a, b) -> a.compareTo(b));

which again doesn't work, contrary to what that article claims that it works. Might be possible that it used to work in some initial builds.

So my question is: Is there any way to create lambda expression for a generic method? I can do this using a method reference though, by creating a method:

public static <T extends Comparable<T>> int compare(T obj1, T obj2) {
    return obj1.compareTo(obj2);
}

in some class say SO, and pass it as:

sort(list, SO::compare);

Java Solutions


Solution 1 - Java

You can't use a lambda expression for a functional interface, if the method in the functional interface has type parameters. See section §15.27.3 in JLS8:

> A lambda expression is compatible [..] with a target type T if T is a functional interface type (§9.8) and the expression is congruent with the function type of [..] T. [..] A lambda expression is congruent with a function type if all of the following are true: > > - The function type has no type parameters. > - [..]

Solution 2 - Java

Using method reference, i found other way to pass the argument:

List<String> list = Arrays.asList("a", "b", "c");        
sort(list, Comparable::<String>compareTo);

Solution 3 - Java

Just point compiler the proper version of generic Comparator with (Comparator<String>)

So the answer will be

sort(list, (Comparator<String>)(a, b) -> a.compareTo(b));

Solution 4 - Java

You mean something like this?:

<T,S>(T t, S s)->...

Of what type is this lambda? You couldn't express that in Java and therefore cannot compose this expression in a function application and expressions have to be composable.

For this need to be work you would need support for Rank2 Types in Java.

Methods are allowed to be generic but therefore you couldn't use them as expressions. They can, however be reduced to lambda expression by specializing all necessary generic types before you can pass them: ClassName::<TypeName>methodName

Solution 5 - Java

List<String> list = Arrays.asList("a", "b", "c");        
sort(list, Comparable::<String>compareTo);

The int compareTo (T o) is not a generic method invocation. Although Comparable<T> is an interface with a type. Even if compareTo had returned T, i.e. T compareTo (T o) it still is not a generic method. For it to be a generic method, it needs to include a list of type parameters, i.e. <T> T compareTo (T o).

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRohit JainView Question on Stackoverflow
Solution 1 - JavanosidView Answer on Stackoverflow
Solution 2 - JavaAndreyView Answer on Stackoverflow
Solution 3 - JavaAleчView Answer on Stackoverflow
Solution 4 - JavaiconflyView Answer on Stackoverflow
Solution 5 - JavaSanjayView Answer on Stackoverflow