Lambda expression
Introduction:
Lambda expressions, also known as closures, are the most important new feature that drives the release of Java 8.
Lambda allows functions to be used as parameters of a method (functions are passed into the method as parameters).
Using Lambda expressions can make the code more concise and compact.
Standard format:
Lambda omits the object-oriented rules and regulations, and the format consists of three parts:
- Some parameters
- An arrow
- A piece of code
The standard format for Lambda expressions is:
(Parameter type parameter name) -> { Code statement }
Format Description:
- The syntax in parentheses is consistent with the parameter list of traditional methods: leave blank if there is no parameter; Multiple parameters are separated by commas.
- ->Is a newly introduced syntax format, which represents pointing action.
- The syntax in braces is basically consistent with the requirements of traditional method body.
Comparison between anonymous inner class and lambda:
new Thread(new Runnable() { @Override public void run() { System.out.println("Multithreaded task execution!"); } }).start();
After careful analysis of the code, the Runnable interface has only one definition of the run method:
- public abstract void run();
That is, a plan for doing things has been formulated (in fact, it is a method):
- No parameter: the scheme can be executed without any conditions.
- No return value: the scheme does not produce any results.
- Code block (method): the specific implementation steps of the scheme.
The same semantics is embodied in Lambda grammar, which should be simpler:
() -> System.out.println("Multithreaded task execution!")
- The first pair of parentheses are the parameters of the run method (none), which means that no conditions are required;
- An arrow in the middle represents passing the previous parameter to the following code;
- The following output statement is the business logic code.
Omit format:
Omission rule
On the basis of Lambda standard format, the rules for using ellipsis are as follows:
- The types of parameters in parentheses can be omitted;
- If there is only one parameter in the parenthesis, the parenthesis can be omitted;
- If there is only one statement in braces, you can omit the braces, return keyword and statement semicolon regardless of whether there is a return value or not.
Note: after mastering these omission rules, please review the multithreading cases at the beginning of this chapter accordingly.
It can be deduced and omitted
Lambda emphasizes "what to do" rather than "how to do", so any information that can be deduced from the context can be omitted. For example, the above example can also use lambda's ellipsis:
Runnable Interface simplification: 1. () -> System.out.println("Multithreaded task execution!") Comparator Interface simplification: 2. Arrays.sort(array, (a, b) -> a.getAge() - b.getAge());
Prerequisites for Lambda
Lambda's syntax is very concise and completely free from the constraints of object-oriented complexity. However, there are several problems that need special attention when using:
- Lambda must have an interface, and there must be only one abstract method in the interface.
Whether it is the built-in Runnable, Comparator interface or user-defined interface of JDK, Lambda can be used only when the abstract methods in the interface exist and are unique. - Using Lambda must have context inference.
That is, the parameter or local variable type of the method must be the interface type corresponding to Lambda, and Lambda can be used as an instance of the interface.
Note: an interface with only one abstract method is called "functional interface".
Lambda expression case:
Define an interface
interface Lambda { /**Multi parameter no return*/ @FunctionalInterface interface NoReturnMultiParam { void method(int a, int b); } /**No parameter, no return value*/ @FunctionalInterface interface NoReturnNoParam { void method(); } /**A parameter is not returned*/ @FunctionalInterface interface NoReturnOneParam { void method(int a); } /**Multiple parameters have return values*/ @FunctionalInterface interface ReturnMultiParam { int method(int a, int b); } /*** Return without parameters*/ @FunctionalInterface interface ReturnNoParam { int method(); } /**A parameter has a return value*/ @FunctionalInterface interface ReturnOneParam { int method(int a); } }
Direct test
public class Test { public static void main(String[] args) { //No parameters, no return Lambda.NoReturnNoParam noReturnNoParam = () -> { System.out.println("NoReturnNoParam");//NoReturnNoParam }; noReturnNoParam.method(); //A parameter is not returned Lambda.NoReturnOneParam noReturnOneParam = (int a) -> { System.out.println("NoReturnOneParam param:" + a);//NoReturnOneParam param:6 }; noReturnOneParam.method(6); //Multiple parameters are not returned Lambda.NoReturnMultiParam noReturnMultiParam = (int a, int b) -> { System.out.println("NoReturnMultiParam param:" + "{" + a +"," + + b +"}");//NoReturnMultiParam param:{6,8} }; noReturnMultiParam.method(6, 8); //No parameter has a return value Lambda.ReturnNoParam returnNoParam = () -> { System.out.print("ReturnNoParam");//ReturnNoParamreturn:1 return 1; }; int res = returnNoParam.method(); System.out.println("return:" + res); //A parameter has a return value Lambda.ReturnOneParam returnOneParam = (int a) -> { System.out.println("ReturnOneParam param:" + a);//ReturnOneParam param:6 return 1; }; int res2 = returnOneParam.method(6); System.out.println("return:" + res2);//return:1 //Multiple parameters have return values Lambda.ReturnMultiParam returnMultiParam = (int a, int b) -> { System.out.println("ReturnMultiParam param:" + "{" + a + "," + b +"}");//ReturnMultiParam param:{6,8} return 1; }; int res3 = returnMultiParam.method(6, 8); System.out.println("return:" + res3);//return:1 } }
Syntax simplification ↓
We can further simplify the code and write more elegant code by observing the following code.
public class Test { public static void main(String[] args) { //1. Simplify parameter types. You can not write parameter types, but you must not write all parameters Lambda.NoReturnMultiParam lamdba1 = (a, b) -> { System.out.println("Simplified parameter type"); }; lamdba1.method(1, 2); //2. Simplify parameter parentheses. If there is only one parameter, parameter parentheses can be omitted Lambda.NoReturnOneParam lambda2 = a -> { System.out.println("Simplified parameter parentheses"); }; lambda2.method(1); //3. Simplify the method body braces. If the method bar has only one statement, you can win the method body braces Lambda.NoReturnNoParam lambda3 = () -> System.out.println("Simplified method body braces"); lambda3.method(); //4. If the method body has only one statement and is a return statement, the method body braces can be omitted Lambda.ReturnOneParam lambda4 = a -> a+3; System.out.println(lambda4.method(5)); Lambda.ReturnMultiParam lambda5 = (a, b) -> a+b; System.out.println(lambda5.method(1, 1)); } }
Common examples of Lambda expressions
lambda expression reference method
Sometimes we don't have to rewrite the method of an anonymous inner class ourselves. We can use the interface of lambda expression to quickly point to an implemented method.
grammar
Method owner: method name. The owner of static method is the class name, and the owner of common method is the object
public class Exe1 { public static void main(String[] args) { ReturnOneParam lambda1 = a -> doubleNum(a); System.out.println(lambda1.method(3)); //lambda2 refers to the already implemented doubleNum method ReturnOneParam lambda2 = Exe1::doubleNum; System.out.println(lambda2.method(3)); Exe1 exe = new Exe1(); //lambda4 refers to the already implemented addTwo method ReturnOneParam lambda4 = exe::addTwo; System.out.println(lambda4.method(2)); } /** * requirement * 1.The number and type of parameters shall be consistent with those defined in the interface * 2.The return value type should be consistent with that defined in the interface */ public static int doubleNum(int a) { return a * 2; } public int addTwo(int a) { return a + 2; } }
Reference to construction method
In general, we need to declare the interface, which is an object generator, instantiate the object by way of class name: new, and then call the method to return the object.
interface ItemCreatorBlankConstruct { Item getItem(); } interface ItemCreatorParamContruct { Item getItem(int id, String name, double price); } public class Exe2 { public static void main(String[] args) { ItemCreatorBlankConstruct creator = () -> new Item(); Item item = creator.getItem(); ItemCreatorBlankConstruct creator2 = Item::new; Item item2 = creator2.getItem(); ItemCreatorParamContruct creator3 = Item::new; Item item3 = creator3.getItem(112, "mouse", 135.99); } }
lambda expression creation thread
In the past, we used to create a Thread object, and then override the run() method through an anonymous inner class. When we mention an anonymous inner class, we should think of using lambda expressions to simplify the Thread creation process.
Thread t = new Thread(() -> { for (int i = 0; i < 10; i++) { System.out.println(2 + ":" + i); } }); t.start();
Traversal set
We can call the public void foreach (Consumer <? Super E > action) method of the collection to traverse the elements in the collection through lambda expressions. The following are the methods of the Consumer interface and the operations of traversing the collection. The Consumer interface is a functional interface provided by jdk.
@FunctionalInterface public interface Consumer<T> { void accept(T t); //.... }
ArrayList<Integer> list = new ArrayList<>(); Collections.addAll(list, 1,2,3,4,5); //lambda expression method reference list.forEach(System.out::println); list.forEach(element -> { if (element % 2 == 0) { System.out.println(element); } });
Delete an element in the collection
We use the public Boolean removeif (predict <? Super E > filter) method to delete an element in the collection. Predict is also a functional interface provided by jdk, which can simplify the programming.
ArrayList<Item> items = new ArrayList<>(); items.add(new Item(11, "Small toothbrush", 12.05 )); items.add(new Item(5, "Japanese toilet cover", 999.05 )); items.add(new Item(7, "Gree air conditioner", 888.88 )); items.add(new Item(17, "soap", 2.00 )); items.add(new Item(9, "Refrigerator", 4200.00 )); items.removeIf(ele -> ele.getId() == 7); //Traverse through foreach to see if it has been deleted items.forEach(System.out::println);
Sorting of elements within a collection
In the past, if we wanted to sort the elements in the collection, we had to call the sort method and pass in the comparator anonymous inner class to override the compare method. Now we can use lambda expressions to simplify the code.
ArrayList<Item> list = new ArrayList<>(); list.add(new Item(13, "vest", 7.80)); list.add(new Item(11, "Half sleeve", 37.80)); list.add(new Item(14, "Windbreaker", 139.80)); list.add(new Item(12, "Autumn pants", 55.33)); /* list.sort(new Comparator<Item>() { @Override public int compare(Item o1, Item o2) { return o1.getId() - o2.getId(); } }); */ list.sort((o1, o2) -> o1.getId() - o2.getId()); System.out.println(list);
Closure problems in Lambda expressions#
This problem also exists in anonymous inner classes. If we release the annotation, we will report an error and tell me that the num value is final and cannot be changed. Although we did not identify the num type as final here, the virtual opportunity helped us add the final modifier keyword during compilation.
import java.util.function.Consumer; public class Main { public static void main(String[] args) { int num = 10; Consumer<String> consumer = ele -> { System.out.println(num); }; //num = num + 2; consumer.accept("hello"); } }