Lambda Expression and generic method

Rohit Jain picture Rohit Jain · Mar 23, 2014 · Viewed 73.3k times · Source

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

Answer

nosid picture nosid · Mar 23, 2014

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.
  • [..]