summary
We use Lambda expressions to implement anonymous methods. But wouldn't it be nice to have an implementation of an anonymous expression and reference it without having to rewrite one expression at a time? Method reference can be done, which makes Lambda's code calling methods that already have method names simpler and easier to understand.
If there is only one line of statement in Lambda, this statement only calls an existing method. As follows:
Arrays.sort(stringsArray,(s1,s2)->s1.compareToIgnoreCase(s2));
In Java 8, we can abbreviate (Reference) existing methods in lambda expressions directly through method references.
Arrays.sort(stringsArray, String::compareToIgnoreCase);
This feature is called method reference.
What is a method reference
A method reference is an existing method or constructor used to directly access a class or instance. Method references provide a way to reference without executing a method, which requires a target type context composed of compatible functional interfaces. When evaluated, the method reference creates an instance of the functional interface.
When only one method call is executed in a Lambda expression, the form of direct method reference without Lambda expression is more readable. Method reference is a more concise and understandable Lambda expression.
Method reference features:
-
Method references point to a method by its name.
-
Method reference can make the language structure more compact and concise and reduce redundant code.
-
Method references use a pair of colons::.
There are four forms of java8 method references:
type | grammar | Corresponding Lambda expression |
---|---|---|
Static method reference | Class name:: staticMethod | (args) - > class name staticMethod(args) |
Instance method reference | inst::instMethod | (args) -> inst.instMethod(args) |
Object method reference | Class name:: instMethod | (Inst, args) - > class name instMethod(args) |
Build method reference | Class name:: new | (args) - > new class name (args) |
1. Static method reference
Composition syntax format: ClassName::staticMethodName
be careful:
- Static method reference is easy to understand. Compared with static method call, static modified method only Replace with:
- Static method references can be used wherever the target type is compatible.
example:
String::valueOf is equivalent to lambda expression (s) - > string valueOf(s)
Math::pow is equivalent to lambda expression (x, y) - > math pow(x, y);
class User { public static String getName() { //static method return "ZhangSan"; } } public class Test { public static void main(String[] args) { Supplier<String> supplier = () -> User.getName();//Traditional Lambda grammar System.out.println(supplier.get()); //Output ZhangSan Supplier<String> supplier2 = User::getName; //Method reference syntax System.out.println(supplier2.get()); //Output ZhangSan } }
2. Instance method reference
Instance method reference, as the name suggests, is to call the method of an existing instance. Unlike static method reference, the class needs to be instantiated first. Static method reference classes do not need to be instantiated and can be called directly with the class name.
class User { public String getName() { //Example method return "ZhangSan"; } } public class Test { public static void main(String[] args) { User user = new User(); //Construct an instance Supplier<String> supplier = () -> user.getName(); //Traditional Lambda grammar System.out.println(supplier.get()); //Output ZhangSan Supplier<String> supplier2 = user::getName; //Method reference syntax System.out.println(supplier2.get()); //Output ZhangSan } }
3. Object method reference (class name: instance method)
I don't know why it is translated into object method reference. This method reference is relatively complex. The syntax is class name:: instance method.
This kind of reference method is a special scenario. Not all instance methods can be called with a class name. We usually do this when calling class methods (static modified methods).
Expected a preceding
Let's look at the cited example:
public static void main(String[] args) { Predicate<String> predicate = x -> x.isEmpty(); //Example, conventional writing Predicate<String> predicate2 = String::isEmpty; //Citation writing ... }
Predicate is a functional interface with an abstract method that receives a parameter and returns a boolean result. X - > x.isEmpty() just matches, but there is another special case of this lambda expression. The first input parameter x is just the caller of the execution statement, and the second input parameter Y is just the input parameter of the instance method isEmpty. Readers will wonder, where is the second participant y? Hehe, we can regard the second input parameter Y as empty, and the input parameter of isEmpty is also empty.
Let's take a look at an example. The input parameters are multiple and real. Let's review the above example:
Arrays.sort(stringsArray,(s1,s2)->s1.compareToIgnoreCase(s2)); //General lambda expression Arrays.sort(stringsArray, String::compareToIgnoreCase); //abbreviation
Let's analyze this example step by step. The sort method of Arrays class is defined as follows:
public static <T> void sort(T[] a, Comparator<? super T> c)
The second parameter of sort() is a Comparator, which is a functional interface. There is an abstract method compare(), and the source code of the Comparator interface in jdk8:
@FunctionalInterface //Declared as a functional interface public interface Comparator<T> { int compare(T o1, T o2); //Two input parameters and one int return value
Therefore, (s1, s2) - > s1 Comparetoignorecase (s2) expression also has two input parameters and an int return value, which conforms to the implementation of Comparator's Lambda form. Let's take a closer look at this expression. Coincidentally, the first input parameter s1 is the caller of the following Lambda body statement, and the second parameter s2 is the input parameter of the Lambda body statement, which conforms to the abbreviated logic String::compareToIgnoreCase
4 construction method reference
Note: the parameter list of the constructor to be called should be consistent with the parameter list of the abstract method in the functional interface.
For example: to get an empty User list:
class People { People() { //Parameterless constructor System.out.println("no param"); } People(String name) { //Constructor with an input parameter System.out.println("param :" + name); } public static void main(String[] args) { Supplier<People> supplier = () -> new People(); //new People() calls the parameterless constructor Supplier<People> supplier2 = People::new; //Call parameterless constructor by reference supplier2.get(); //Print no param to prove that the parameterless constructor is called } Function<String, People> supplier3 = x -> new People(x); //Call the constructor with input parameters Function<String, People> supplier4 = People::new; //Call the constructor with parameters by reference supplier4.apply("zhangsan"); //Param: Zhangsan, proving that the constructor with parameters is called }
The reference of constructor will automatically match the constructor with the same parameters when multiple constructors are used