import java.util.*;
class TreeMapDemo
{
public static void main(String args[])
{
Comparator <String> c1 = (str1, str2) -> 0;
Comparator <String> c2 = (str1, str2) -> 1;
TreeMap <String, Double> tm1 = new TreeMap(c1.thenComparing(c2));
//Working fine
TreeMap <String, Double> tm2 = new TreeMap(((str1, str2) -> 0).thenComparing((str1, str2) -> 1));
//Error: Lambda expression not expected here
//<none> can not be dereferenced
}
}
My query is:
If
c1 = (str1, str2) -> 0
and c2 = (str1, str2) -> 1
,
Then why
c1.thenComparing(c2)
is working fine and
((str1, str2) -> 0).thenComparing((str1, str2) -> 1)
is not?
Java relies on the context of a lambda expression to determine its type. Assigning a lambda to a variable or passing it as a method argument provides sufficient context, but invoking a method -- such as thenComparing()
-- on a lambda provides no useful context at all.
This should work:
Comparator <String> c1 = (str1, str2) -> 0;
TreeMap <String, Double> tm2 = new TreeMap(c1.thenComparing((str1, str2) -> 1));
In more detail:
A lambda expression evaluates to an object of a type that implements some functional interface, and Java relies on the context in which the expression appears to determine which functional interface that is. In your second case, the constructor argument is not the lambda's context; the invocation of thenComparing()
is. It is that method's return value that is the constructor argument. But before Java can determine anything about thenComparing()
it needs to know the type of the object on which it is being invoked. The type informs about the method, not the other way around.
Java cannot work backwards from the argument type to the needed type of the lambda because there could be any number of functional interfaces that would work. Java cannot assume that the wanted interface is in the standard library.