Functional Programming and Lambda Expression
Advantages of 1 Functional Programming
1.1 Functional Programming Ideas
In mathematics, a function is a set of calculation schemes with inputs and outputs, that is, "What to do with something?" (What to do with something?) (What to do with something?) (What to do with something?) Functions in programming have similar concepts. When you call me, give me arguments to assign values to parameters, and then run the body of the method to return you a result. For callers, focus on what this method does. Relatively, object-oriented places too much emphasis on "must do things in the form of objects", while functional thinking tries to ignore the complex object-oriented syntax, which emphasizes what to do rather than what to do in the form.
-
Object-oriented thinking:
- Do one thing, find an object that can solve it, call the method of the object, and finish the thing.
-
Functional programming ideas:
- As long as the result can be obtained, it does not matter who does it, what is important is the result, and what is not the process.
After Java 8 introduced Lambda expressions, Java also began to support functional programming.
Lambda expressions are not the earliest used in Java and are supported in many languages, such as C++, C#, Python, Scala, and so on. If you have a Python or Javascript language base, it will be very helpful to understand Lambda expressions. In other words, lambda expressions are grammatical sugars that implement SAM interfaces, making Java a language that supports functional programming. Lambda's good writing greatly reduces code redundancy, as well as readability over redundant anonymous internal classes.
Note: "Grammatical sugar" refers to code grammar that is more convenient to use but does not change in principle. For example, the for-each syntax used when traversing a set is
The underlying implementation principle is still iterator, which is grammatical sugar. On an application level, Lambda in Java can be considered anonymous internals
Class "grammatical sugar", but they are different in principle.
Scanning VX for Java, Front End, Testing, python, etc.
1,2 Redundant Anonymous Internal Classes
When a thread needs to be started to complete a task, it usually takes place in java. The lang.Runnable interface defines the task content and uses java. The lang.Thread class to start the thread. The code is as follows:
Copy code-behind code public class Demo01 { public static void main(String[] args) { Runnable runnable = new Runnable() { @Override public void run() {// Override override abstract methods System.out.println("Override with anonymous internal class run Method"); } }; new Thread(runnable).start(); // Start Thread new Thread(new Runnable() { @Override public void run() { System.out.println("Do not create object overrides using anonymous internal classes run Method"); } }).start(); } }
With the idea of "Everything is Object", this is justifiable: first create an anonymous internal class object for the Runnable interface to specify the task content, and then hand it over to a thread to start.
Code Analysis:
For Runnable's anonymous internal class usage, there are a few things you can analyze:
- The Thread class requires the Runnable interface as a parameter, and the abstract run method is the core of the thread task content specified.
- In order to specify the body of run's method, the implementation class of the Runnable interface has to be required.
- To avoid the hassle of defining a RunnableImpl implementation class, you have to use an anonymous internal class;
- The override of the abstract run method must be overridden, so the method name, method parameters, method return values must be rewritten and cannot be written incorrectly.
- In fact, it seems that only the method body is the key.
1.3 Conversion of Programming Ideas
What to do, not who to do it, how to do it
Do we really want to create an anonymous internal class object? No We just have to create an object to do this. What we really want to do is pass code from the run method body to the Thread class.
Pass a piece of code - that's what we really want. Creating objects is only a means that has to be taken because of the object-oriented syntax. So, is there a simpler way? If we return our focus from how to do to what is essentially what to do, we will find that process and form are not really important as long as we can achieve our goals better.
Examples of life:
When we need to go from Beijing to Shanghai, we can choose high-speed rail, car, bike or hiking. Our real goal is to get to Shanghai, and how we get there doesn't matter, so we've been exploring whether there's a better way than high-speed rail - by airplane.
Now this kind of airplane (or even a spaceship) has been born: Oracle's Java 8 (JDK 1.8) in March 2014 added the new weight feature of Lambda expression, opening the door to a new world.
1.4 Experience Lambda's Better Writing
With the new Java 8 syntax, the anonymous internal class notation of the above Runnable interface can be equivalent through simpler Lambda expressions:
Copy code-behind code public class Demo01 { public static void main(String[] args) { new Thread(() -> { System.out.println("Use Lambda Expression override run Method"); }).start(); new Thread(() -> System.out.println("Use simplified Lambda Expression override run Method")).start(); } }
This code is exactly the same as what was just executed and can be passed at a compilation level of 1.8 or higher. You can see from the semantics of the code that we started a thread, and the content of the thread task was specified in a more concise form.
There is no longer the constraint of having to create interface objects and the burden of overriding overrides with abstract methods. It's that simple!
2 Functional Interface
The lambda expression is actually a statement that implements the SAM interface, which is called Single Abstract Method, that is, there is only one abstract method in the interface that needs to be implemented, although the interface can contain other non-abstract methods.
In fact, Lambda expressions can be used whenever an interface that meets the SAM characteristics can be called a functional interface, but to be clearer, it is better to declare the interface with @FunctionalInterface. Once the annotation is used to define the interface, the compiler will force a check that the interface does have and only has one abstract method, otherwise an error will be reported.
In previous SAM interfaces, the functional interfaces marked @FunctionalInterface were Runnable, Comparator, FileFilter.
Java8 is in java.util.function has added many new functional interfaces: mainly divided into four categories, consumption, supply, judgment and function. Basic to meet our development needs. Of course, you can also define your own functional interface.
2.1 Custom Functional Interface
Just make sure there is only one abstract method in the interface:
Copy code-behind code Modifier interface Interface Name { public abstract Return value type method name(Optional parameter information); // Other non-abstract method content }
public abstract of abstract methods in interfaces can be omitted
For example, declare a calculator Calculator interface that contains an abstract method, calc, that calculates two ints and returns the result:
Copy code-behind code public interface Calculator { int calc(int a, int b); }
In the test class, declare one of the following methods:
Copy code-behind code public static void invokeCalc(int a, int b, Calculator calculator) { int result = calculator.calc(a, b); System.out.println("The result is:" + result); }
The following tests are performed:
Copy code-behind code public static void main(String[] args) { invokeCalc(1, 2, (int a,int b)-> {return a+b;}); invokeCalc(1, 2, (int a,int b)-> {return a-b;}); invokeCalc(1, 2, (int a,int b)-> {return a*b;}); invokeCalc(1, 2, (int a,int b)-> {return a/b;}); invokeCalc(1, 2, (int a,int b)-> {return a%b;}); invokeCalc(1, 2, (int a,int b)-> {return a>b?a:b;}); }
Classification of 2.2 Functional Interfaces
In addition to the functional interfaces that we can customize, jdk also gives us some built-in functional interfaces, categorized as follows
2.2. 1 Consumer Interface
Abstract method features of consumer interfaces: formal parameters, but return value type is void
Interface name | Abstract method | describe |
---|---|---|
Consumer<T> | void accept(T t) | Receive an object to complete a function |
BiConsumer<T,U> | void accept(T t, U u) | Receive two objects to complete the function |
DoubleConsumer | void accept(double value) | Receive a double value |
IntConsumer | void accept(int value) | Receive an int value |
LongConsumer | void accept(long value) | Receive a long value |
ObjDoubleConsumer<T> | void accept(T t, double value) | Receive an object and a double value |
ObjIntConsumer<T> | void accept(T t, int value) | Receive an object and an int value |
ObjLongConsumer<T> | void accept(T t, long value) | Receive an object and a long value |
2.2. 2 Supply Interface
The abstract method features of this type of interface: no parameters, but return values
Interface name | Abstract method | describe |
---|---|---|
Supplier<T> | T get() | Return an object |
BooleanSupplier | boolean getAsBoolean() | Returns a boolean value |
DoubleSupplier | double getAsDouble() | Returns a double value |
IntSupplier | int getAsInt() | Returns an int value |
LongSupplier | long getAsLong() | Returns a long value |
2.2. 3 Assertive Interface
The abstract method feature of the interface here is that it has parameters, but the return value type is a boolean result.
Interface name | Abstract method | describe |
---|---|---|
Predicate<T> | boolean test(T t) | Receive an object |
BiPredicate<T,U> | boolean test(T t, U u) | Receive two objects |
DoublePredicate | boolean test(double value) | Receive a double value |
IntPredicate | boolean test(int value) | Receive an int value |
LongPredicate | boolean test(long value) | Receive a long value |
2.2. 4 Functional Interface
The abstract method features of this type of interface: both parameters and return values
Interface name | Abstract method | describe |
---|---|---|
Function<T,R> | R apply(T t) | Receives a T-type object and returns an R-type object result |
UnaryOperator<T> | T apply(T t) | Receives a T-type object and returns a T-type object result |
DoubleFunction<R> | R apply(double value) | Receives a double value and returns an object of type R |
IntFunction<R> | R apply(int value) | Receives an int value and returns an object of type R |
LongFunction<R> | R apply(long value) | Receive a long value and return an object of type R |
ToDoubleFunction<T> | double applyAsDouble(T value) | Receive a T-type object and return a double |
ToIntFunction<T> | int applyAsInt(T value) | Receives a T-type object and returns an int |
ToLongFunction<T> | long applyAsLong(T value) | Receive a T-type object and return a long |
DoubleToIntFunction | int applyAsInt(double value) | Receives a double value and returns an int result |
DoubleToLongFunction | long applyAsLong(double value) | Receive a double value and return a long result |
IntToDoubleFunction | double applyAsDouble(int value) | Receives an int value and returns a double result |
IntToLongFunction | long applyAsLong(int value) | Receive an int value and return a long result |
LongToDoubleFunction | double applyAsDouble(long value) | Receive a long value and return a double result |
LongToIntFunction | int applyAsInt(long value) | Receives a long value and returns an int result |
DoubleUnaryOperator | double applyAsDouble(double operand) | Receive a double value and return a double |
IntUnaryOperator | int applyAsInt(int operand) | Receives an int value and returns an int result |
LongUnaryOperator | long applyAsLong(long operand) | Receive a long value and return a long result |
BiFunction<T,U,R> | R apply(T t, U u) | Receives a T-type and a U-type object and returns an R-type object result |
BinaryOperator<T> | T apply(T t, T u) | Receives two T-type objects and returns a T-type object result |
ToDoubleBiFunction<T,U> | double applyAsDouble(T t, U u) | Receives a T-type and a U-type object and returns a double |
ToIntBiFunction<T,U> | int applyAsInt(T t, U u) | Receives a T-type and a U-type object and returns an int |
ToLongBiFunction<T,U> | long applyAsLong(T t, U u) | Receives a T-type and a U-type object and returns a long |
DoubleBinaryOperator | double applyAsDouble(double left, double right) | Receives two double values and returns a double result |
IntBinaryOperator | int applyAsInt(int left, int right) | Receives two ints and returns an int result |
LongBinaryOperator | long applyAsLong(long left, long right) | Receives two long values and returns a long result |
3 Lambda expression syntax
Lambda expressions are used to assign values to variables or parameters of [functional interfaces].
Essentially, Lambda expressions are "abstract methods" used to implement [functional interfaces]
Lambda expression syntax format
Copy code-behind code (parameter list) -> {Lambda body}
Explain:
- (parameter list) It is the abstract method of the functional interface you want to assign (parameter list), refer to
- {Lambda body} is the body of the method that implements this abstract method
- ->Called the Lambda operator (there must be no space between the minus sign and the greater sign, and it must be a half-corner input in English)
Optimize: Lambda expressions can be streamlined
- When there is only one sentence in {Lambda body}, you can omit {} and {;}
- When there is only one sentence in the {Lambda body} and the statement is still a return statement, return can also be omitted, but if it is {;} Return cannot be omitted without omitting
- The type of (parameter list) can be omitted
- When there is only one parameter in the list, the data type can be omitted with (), but the parameter name cannot be omitted.
- When (parameter list) is empty, () cannot be omitted
Sample code:
ps: The following demo uses Junit for unit testing, so if you don't understand it, you can learn it first Junit
Copy code-behind code public class TestLambdaGrammer { @Test public void test1(){ //Apply Lambda expression to parameter or variable of Runnable interface /* * There are two things you can do to write a lambda expression * (1)Does the abstract method of this interface require an incoming parameter * public void run() * (2)What will the implementation of this abstract method do? * For example: I want to print "hello lambda" */ Runnable r = () -> {System.out.println("hello lambda");}; //Simplify, omit {;} Runnable r = () -> System.out.println("hello lambda"); } @Test public void test2(){ String[] arr = {"hello","Hello","java","chai"}; //Sort arr arrays, but want to be case insensitive /* * public static <T> void sort(T[] a,Comparator<? super T> c) * Lambda expression is used here to assign values to parameters of type Comparator * * Two things: * (1)Abstract method of this interface: int compare(T o1, T o2) * (2)What does this abstract method do? * For example, here we want to make case-insensitive comparisons for elements of type String */ Arrays.sort(arr, (String o1, String o2) -> {return o1.compareToIgnoreCase(o2);}); //{return;} was omitted //Arrays.sort(arr, (String o1, String o2) -> o1.compareToIgnoreCase(o2)); //Two String s were omitted Arrays.sort(arr, (o1, o2) -> o1.compareToIgnoreCase(o2)); //Print arr for (String string : arr) { System.out.println(string); } } @Test public void test3(){ ArrayList<String> list = new ArrayList<>(); list.add("hello"); list.add("java"); list.add("world"); /* * JDK1.8 A default method has been added to the Collection of Collections, precisely in the Iterable interface * default void forEach(Consumer<? super T> action) * This method is used to traverse collections, etc. Instead of the original foreach loop. * * The parameter to this method is the Consumer interface type, which represents the consumer interface in a functional interface * Now call this method, and you want to use the Lambda expression to assign a value to the Consumer interface type parameter * * Two things: * (1)Its Abstract method: void accept(T) * (2)What is the accomplishment of an abstract method * For example, to print this t here */ // list.forEach((String t) -> {System.out.println(t);}); //Omit {;} // list.forEach((String t) -> System.out.println(t)); //Omit String // list.forEach((t) -> System.out.println(t)); //Omitted () list.forEach(t -> System.out.println(t)); } }
Scanning VX for Java, Front End, Testing, python, etc.
4 Lambda Expression Practice
4.1 Use of consumer interface
4.1. 1 Custom Consumer Interface Test
Define interfaces:
Copy code-behind code package Jdk8.Lambda; import java.util.function.Consumer; public class LambdaManager { public static void userConsumer(String str, Consumer<String> consumer) { consumer.accept(str); } }
Interface testing:
Copy code-behind code //Custom Consumer Interface @Test public void testConsumer() { //LambdaManager.userConsumer("Hello", new Consumer <String>() {//Implement interfaces using anonymous internal classes // @Override // public void accept(String s) { // System.out.println(s); // } //};// Same as below LambdaManager.userConsumer("Hello", (str) -> System.out.println(str)); }
4.1. Consumer Interface Testing in 2 JDK
Code example: Consumer<T>interface
In JDK1. A default method has been added to the parent Iterable interface of the Collection collection interface in 8:
Public default void for Each (Consumer<? Super T> action) iterates through each element of the Collection collection and performs an "xxx consumer" operation.
In JDK1. A default method has been added to the Map Collection interface in 8:
Public default void for Each (BiConsumer<? Super K,? Super V> action) iterates through each pair of mapping relationships in the Map collection to perform an "xxx consumer" operation.
Case:
(1) Create a collection of Collection series, add programming languages you know, and call the forEach method to iterate through the view
(2) Create a collection of Map series, add some (key,value) key-value pairs, for example, add programming language rankings and language names, call the forEach method to iterate through the view
Sample code:
Copy code-behind code @Test//Consumer interface testing in jdk public void testJdkConsumer() { //Create a List Collection List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9); //Traverse through each element of the collection // for (Integer integer : list) { // System.out.println(integer); // } // list.forEach(new Consumer<Integer>() { // @Override // public void accept(Integer integer) { integer Is every element traversed,Can print directly // } //}; Jdk already has a foreach defined inside, just call it directly list.forEach(i -> System.out.println(i)); HashMap<Integer, String> map = new HashMap<>(); map.put(1, "Java"); map.put(2, "C"); map.put(3, "C++"); map.put(4, "JavaScript"); map.put(5, "c#"); map.put(6, "Python"); map.put(7, "PHP"); map.put(8, "Matlab"); map.put(9, "Go"); // map.forEach(new BiConsumer<Integer, String>() { // @Override // public void accept(Integer integer, String s) { //System.out.println("number" + integer + "number, value" + s); // } // }); map.forEach((i, s) -> System.out.println("No." + i + "individual,Value is" + s)); }
4.2 Supply Interface
4.2. 1 Custom Supply Interface Test
Define interfaces:
Copy code-behind code package Jdk8.Lambda1; import java.util.function.Supplier; public class LambdaManager { public static void userSupplier(Supplier<String> supplier) { String res = supplier.get(); System.out.println("The value of the call to the supply interface is" + res); } }
Interface testing:
Copy code-behind code //Supply Interface @Test public void testSupplier() { // LambdaManager.userSupplier(new Supplier<String>() { // @Override // public String get() { // return "xiaogao"; // } //};// Same as below LambdaManager.userSupplier(() -> "xiaogao"); }
4.2. Supply Interface Test in 2 JDK
Code example: Supplier<T>interface
In JDK1. Stream API, java, was added in 8. Util. Stream. Stream<T>is a data stream. This type has a static method:
Public static <T> Stream <T> generate (Supplier <T> s) creates Stream objects. It also contains a forEach method that traverses elements in the stream: public void for Each (Consumer<? Super T> action).
Case:
Now call Stream's generate method to produce a stream object and call Math. The random() method generates data and assigns values to the parameters of the Supplier functional interface. Finally, the forEach method is called to iterate through the data in the stream to see the results.
Copy code-behind code @Test public void testJdkSupplier(){ Stream.generate(() -> Math.random()).forEach(num -> System.out.println(num)); }
4.3 Functional Interface
4.3. 1 Custom Functional Interface Test
Define interfaces:
Copy code-behind code package Jdk8.Lambda; import java.util.function.Function; public class LambdaManager { public static void userFunction(String str, Function<String, String> function) { String apply = function.apply(str); System.out.println("The result returned is" + apply); } }
Interface testing:
Copy code-behind code //Functional interfaces @Test public void testFunction() { // LambdaManager.userFunction("ming", new Function<String, String>() { // @Override // public String apply(String s) { //return s += "Hello"; // } // }); LambdaManager.userFunction("uzi", (s -> s += "How do you do")); //Hello uzi, the result returned }
4.3. Functional Interface Testing in 2 JDK
Code example: Funtion<T, R>Interface
In JDK1. The Map interface adds many methods at 8:00, such as:
Public default void replaceAll (BiFunction<? Super K,? Super V,? Extends V> function) replaces the value in the map as specified by the function.
Public default void for Each (BiConsumer<? Super K,? Super V> action) iterates through each pair of mapping relationships in the Map collection to perform an "xxx consumer" operation.
Copy code-behind code @Test//Functional interface testing in jdk public void testJdkFunction() { HashMap<Integer, String> map = new HashMap<>(); map.put(1, "Java"); map.put(2, "C"); map.put(3, "C++"); map.put(4, "JavaScript"); map.put(5, "c#"); map.put(6, "Python"); map.put(7, "PHP"); map.put(8, "Matlab"); map.put(9, "Go"); //Objectives: Replace the name of the java discipline with the name of the java Web /* for (Map.Entry<Integer, String> entry : map.entrySet()) { Integer key=entry.getKey(); String value=entry.getValue(); if (value.equals("Java")) { map.put(key,"JavaWeb"); } }//General Writing*/ /*map.replaceAll(new BiFunction<Integer, String, String>() { @Override public String apply(Integer k, String v) { // Determine if v is java if (v.equals("Java")) { return "JavaWeb"; } return v; } });//Anonymous Internal Class Writing*/ map.replaceAll((k, v) -> { if (v.equals("Java")) { return "JavaWeb"; } return v; });//Lambda Expression Writing map.forEach((i, s) -> System.out.println("No." + i + "individual,Value is" + s));//Print with foreach }
4.4 Assertive Interface
4.4. 1 Custom Assertive Interface Test
Define interfaces:
Copy code-behind code package Jdk8.Lambda; import java.util.function.Predicate; public class LambdaManager { public static void userPredicate(String name, Predicate<String> predicate) { boolean flag = predicate.test(name); System.out.println(flag); } }
Interface testing:
Copy code-behind code //Assertive Interface @Test public void testPredicate() { // LambdaManager.userPredicate("uzi", new Predicate<String>() { // @Override // public boolean test(String s) { // return s.equals("ming"); // } // }); LambdaManager.userPredicate("ming", (s -> s.equals("ming"))); }
4.4. 2. jdk interrupt interface testing
Code example: Predicate<T>interface
JDK1. At 8:00, the Collecton <E>interface added one of the following methods:
Public default Boolean removeIf (Predicate<? Super E> filter) is used to delete conditional judgments in a collection that meet the criteria specified by the filter.
Public default void for Each (Consumer<? Super T> action) iterates through each element of the Collection collection and performs an "xxx consumer" operation.
Copy code-behind code @Test public void testJdkPredicate() { /* 1)Add some strings to a Collection collection (2)Call forEach to traverse the collection (3)Call the removeIf method and delete the (4)Call forEach again to traverse the collection*/ ArrayList<String> list = new ArrayList<>(); list.add("Java"); list.add("C"); list.add("Python"); list.add("PHP"); list.add("JavaScript"); //Call forEach to traverse the collection list.forEach(s -> System.out.println(s)); //Call the removeIf method and delete the list.removeIf(s -> { if (s.length() < 5) { return true; } return false; }); System.out.println("--------------"); //Call forEach again to traverse the collection list.forEach(s -> System.out.println(s)); }
4.5 Cases
(1) Declare an Employee type including number, name, gender, age, and salary.
(2) Declare an EmployeeSeries employee management class containing all attributes of an ArrayList <Employee>collection, and create some employee objects in the EmployeeSeries constructor to initialize the all collection.
(3) In the EmployeeSeries employee management class, declare a method: ArrayList <Employee> get (Predicate <Employee> p), which adds an employee who meets the conditions specified by P to a new ArrayList <Employee> collection and returns it.
(4) Create an object in the EmployeeSeries employee management class in the test class and call the get method to get:
- All Employee Objects
- All employees over 35 years of age
- All female employees with salaries above 15,000
- All employees with even numbers
- Employee with the name "Zhang San"
- Male Employees Aged Over 25 and Salary Under 10,000
Sample code:
Employee class:
Copy code-behind code package com.itheima.pojo; public class Employee{ private int id; private String name; private char gender; private int age; private double salary; public Employee(int id, String name, char gender, int age, double salary) { super(); this.id = id; this.name = name; this.gender = gender; this.age = age; this.salary = salary; } public char getGender() { return gender; } public void setGender(char gender) { this.gender = gender; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Employee() { super(); } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } @Override public String toString() { return "Employee [id=" + id + ", name=" + name + ", gender=" + gender + ", age=" + age + ", salary=" + salary + "]"; } }
Employee Management:
Copy code-behind code class EmployeeService{ private ArrayList<Employee> all; public EmployeeService(){ all = new ArrayList<Employee>(); all.add(new Employee(1, "Zhang San", 'male', 33, 8000)); all.add(new Employee(2, "Jade Blossom", 'female', 23, 18000)); all.add(new Employee(3, "You're incompetent", 'male', 46, 8000)); all.add(new Employee(4, "Li Si", 'female', 23, 9000)); all.add(new Employee(5, "King", 'male', 23, 15000)); all.add(new Employee(6, "Big mouth", 'male', 23, 11000)); } public ArrayList<Employee> get(Predicate<Employee> p){ ArrayList<Employee> result = new ArrayList<Employee>(); for (Employee emp : all) { if(p.test(emp)){ result.add(emp); } } return result; } }
Test class:
Copy code-behind code public class TestLambda { public static void main(String[] args) { //All employees over 35 years of age EmployeeManager employeeManager = new EmployeeManager(); List<Employee> employeeList1 = employeeManager.find(e -> e.getAge() > 35); employeeList1.forEach(e -> System.out.println(e)); //All female employees with salaries above 15,000 List<Employee> employeeList2 = employeeManager.find(e -> e.getSalary() > 15000 && e.getGender() == 'female'); employeeList2.forEach(e -> System.out.println(e)); //All employees with even numbers List<Employee> employeeList3 = employeeManager.find(e -> e.getId() % 2 == 0); employeeList2.forEach(e -> System.out.println(e)); //Employee with the name "Zhang San" List<Employee> employeeList4 = employeeManager.find(e -> e.getName() == "Zhang San"); employeeList4.forEach(e -> System.out.println(e)); //Male Employees Aged Over 25 and Salary Under 10,000 List<Employee> employeeList5 = employeeManager.find(e -> e.getAge() > 25 && e.getSalary() < 10000 && e.getGender() == 'male'); employeeList5.forEach(e -> System.out.println(e)); } }
5 Method References and Constructor References
Lambda expressions are syntax that simplifies the assignment of variables and parameters to a functional interface. Method and constructor references are used to simplify Lambda expressions. Lambda expressions can also be simplified when they satisfy some special cases:
(1) Lambda body has only one statement and is accomplished by calling an existing method of an object/class
For example: System.out object, calling println() method to complete Lambda body
Copy code-behind code Math Class, call random()Static method complete Lambda body
(2) and the parameter of the Lambda expression is exactly the argument given to the method
For example: t->System. Out. Println(t)
Copy code-behind code () -> Math.random() All without parameters
5.1 Method Reference
Syntax format of method reference:
(1) Instance object name: Instance method
(2) Class name:: Static method
(3) Class name:: Instance method
Explain:
- :: called method reference operator (two: no spaces in the middle, and must be entered in half-corner in English)
- The list of parameters for a Lambda expression is used in the Lambda body, either as an object to call a method or as an argument to a method.
- There is no additional data in the entire Lambda body.
Copy code-behind code @Test public void test1(){ // Runnable r = () -> System.out.println("hello lambda"); Runnable r = System.out::println;//Print blank lines //Method references cannot be simplified because "hello lambda" cannot be omitted } @Test public void test2(){ String[] arr = {"Hello","java","chai"}; // Arrays.sort(arr, (s1,s2) -> s1.compareToIgnoreCase(s2)); //Simplify by Method Reference /* * Lambda The first parameter of the expression, for example, s1, happens to be the object calling the method, and the remaining parameter (for example, s2) happens to be the argument given to the method */ Arrays.sort(arr, String::compareToIgnoreCase); } @Test public void test3(){ // Stream<Double> stream = Stream.generate(() -> Math.random()); //Simplify by Method Reference Stream<Double> stream = Stream.generate(Math::random); } @Test public void test4(){ List<Integer> list = Arrays.asList(1,3,4,8,9); //list.forEach(t -> System.out.println(t)); //Method Resimplification list.forEach(System.out::println); }
5.2 Constructor References
(1) When a Lambda expression creates an object and satisfies the Lambda expression parameters, it is exactly a list of arguments to the constructor that created the object.
(2) When a Lambda expression creates an array object and satisfies the Lambda expression parameter, it is exactly the length of the array object created
The syntax format referenced by the constructor:
- Class name:new
- Array type name::new
Sample code:
Copy code-behind code public class TestMethodReference { @Test public void test1() { Stream<Integer> stream = Stream.of(1,2,3); Stream<int[]> stream2 = stream.map(int[]::new); } @Test public void test2() { Stream<String> stream = Stream.of("1.0","2.3","4.4"); // Stream<BigDecimal> stream2 = stream.map(num -> new BigDecimal(num)); Stream<BigDecimal> stream2 = stream.map(BigDecimal::new); } @Test public void test3(){ //Supplier <String> s = () -> new String (); // Provide an empty string object through a Supply-type interface //Constructor Reference Supplier<String> s = String::new;//Provide an empty string object through a Supply-type interface } }
Scanning VX for Java, Front End, Testing, python, etc.