I am currently learning lambda expressions on JDK 1.8. I have come across some code I have found that I do not understand.
Here is the code:
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.lang.Comparable;
/**
* Hello world!
*
*/
public class App
{
public static void main( String[] args ) throws Exception
{
List<String> list = Arrays.asList("a", "b", "c");
sort(list, Comparable::<String>compareTo);
}
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) {
int n = comp.compare("5","2");
System.out.println(n);
}
}
comp.compare("5", "3")
eventually executes "5".compareTo("2")
.
My understanding was the compiler needs to find a static method with same signature as
public <T extends Comparable<T>> int compare(T obj1, T obj2 );
I have created such a method and it works. I do not understand why the java compiler calls "5".compareTo("2")
. Their method signitures are not the same.
Any information as to why the compiler generates this kind of code?
If you are trying to learn method references, you should resort to some sort of learning material, e.g. Oracle’s Java tutorial. There you find:
Kinds of method references
Kind Example
- Reference to a static method
ContainingClass::staticMethodName
- Reference to an instance method of a particular object
containingObject::instanceMethodName
- Reference to an instance method of an arbitrary object of a particular type
ContainingType::methodName
- Reference to a constructor
ClassName::new
So you see, method references are not restricted to static
methods.
Your method reference Comparable::<String>compareTo
matches the kind “Reference to an instance method of an arbitrary object of a particular type”.
At this point it’s worth noting that you are actually referencing the method Comparable.compareTo
as if you had written Comparable::compareTo
. Since the referenced method has no type parameters on its own, your type argument has no effect. E.g. you could write Comparable::<Button>compareTo
with the same result.
The referenced method has the functional signature (Comparable,Comparable) → int
as it consumes two Comparable
s when invoking Comparable.compareTo
on one Comparable
, passing the second Comparable
as argument (and it will return an int
). This matches the functional signature of your interface
interface MyComparable {
public <T extends Comparable<T>> int compare(T obj1, T obj2 );
}
so the method reference can be used in this context.
I have simplified the functional signatures; actually they are (T,T)→int
using <T extends Comparable<T>>
, therefore you can only compare two instances of the same concrete Comparable
implementation using this function.
So you're wondering how a method reference with different signature than expected, can be sent as lambda expression. But they don't need to be fully the same. Basically, only list of parameters and return type matters:
A lambda expression can be assigned to a target type T if all of the following conditions hold:
T is a functional interface type
The lambda expression has the same number of parameters as T's method, and those parameters' types are the same
Each expression returned by the lambda body is compatible with T's method's return type
Each exception thrown by the lambda body is allowed by T's method's throws clause
List of parameters of your compare()
(String
, String
) can be matched to list of parameters of String#compareTo()
(this
, String
) and their return types are also the same (int
), so one can be used as lambda when another is expected.
来源:https://stackoverflow.com/questions/29802633/java-lambda-expression