Ctrip four sides: talk about the evolution process of Lambda expression!

preface

The basis for using Lambda is that there must be a corresponding function interface (function interface refers to the interface with only one abstract method inside). This is consistent with the fact that Java is a strongly typed language, which means that you can't write Lambda expressions arbitrarily anywhere in the code. In fact, the type of Lambda is the type of the corresponding function interface. Another basis of Lambda expression is the type inference mechanism (emphasis). When the context information is sufficient, the compiler can infer the type of parameter table without explicit naming.

Reader benefits: Java core learning notes +2021 latest Real interview questions of large factories share!

1, Evolution process

A. Preparation of basic classes

package com.os.model;
import java.util.Objects;
public class Employee {
	private int id;
	private String name;
	private int age;
	private double salary;
	//Omit the generated getter and setter methods, and construct methods, toString methods, hashCode and equals methods
}
Copy code

B. Filter data

We need to set different methods according to different screening conditions, which increases a lot of code.

package com.os.test;

import com.os.model.Employee;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Demo01 {
	private static List<Employee> emps = Arrays.asList(
			new Employee(101, "name of a fictitious monkey with supernatural powers", 18, 9999.99),
			new Employee(102, "Bajie", 59, 6666.66),
			new Employee(103, "Tang Monk", 28, 3333.33),
			new Employee(104, "Monk Sha", 8, 7777.77),
			new Employee(105, "White dragon horse", 38, 5555.55)
	);
	public static void main(String[] args) {
		System.out.println("===>1.Screening age");
		List<Employee> list = filterEmployeeAge(emps);
		for (Employee employee : list) {
			System.out.println(employee);
		}
		System.out.println("===>2.Screening salary");
		list = filterEmployeeSalary(emps);
		for (Employee employee : list) {
			System.out.println(employee);
		}
	}
	/**
	 * Demand: obtain the information of employees younger than 35 in the company
	 * @param employeeList
	 * @return
	 */
	public static List<Employee> filterEmployeeAge(List<Employee> employeeList){
		List<Employee> list = new ArrayList<>();

		for (Employee emp : employeeList) {
			if(emp.getAge() <= 35){
				list.add(emp);
			}
		}
		return list;
	}

	/**
	 * Demand: obtain the information of employees whose salary is greater than 5000 in the company
	 * @param employeeList
	 * @return
	 */
	public static List<Employee> filterEmployeeSalary(List<Employee> employeeList){
		List<Employee> list = new ArrayList<>();

		for (Employee emp : employeeList) {
			if(emp.getSalary() >= 5000){
				list.add(emp);
			}
		}

		return list;
	}
}
Copy code

C. Code evolution: strategy design pattern

(1) Generic interface for defining filter criteria

package com.os.service;
/**
 * Interface for data filtering criteria
 */
public interface ObjectDataPredicate<T> {
	boolean test(T t);
}
Copy code

(2) Implement classes to implement different filtering methods

Filter implementation classes by age

package com.os.service;

import com.os.model.Employee;

public class FilterEmployeeForAge implements ObjectDataPredicate<Employee> {
	@Override
	public boolean test(Employee employee) {
		return employee.getAge() <= 35;
	}
}
Copy code

Filter implementation classes by salary

package com.os.service;

import com.os.model.Employee;

public class FilterEmployeeForSalary implements ObjectDataPredicate<Employee> {
	@Override
	public boolean test(Employee employee) {
		return employee.getSalary() >= 5000;
	}
}
Copy code

(3) Implementation code of policy mode

public static List<Employee> filterEmployee(List<Employee> emps, ObjectDataPredicate<Employee> objectDataPredicate){
    List<Employee> list = new ArrayList<>();
    for (Employee employee : emps) {
        if(objectDataPredicate.test(employee)){
            list.add(employee);
        }
    }
    return list;
}
Copy code

The complete code is as follows

package com.os.test;

import com.os.model.Employee;
import com.os.service.FilterEmployeeForAge;
import com.os.service.FilterEmployeeForSalary;
import com.os.service.ObjectDataPredicate;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Demo02 {
    private static List<Employee> emps = Arrays.asList(
        new Employee(101, "name of a fictitious monkey with supernatural powers", 18, 9999.99),
        new Employee(102, "Bajie", 59, 6666.66),
        new Employee(103, "Tang Monk", 28, 3333.33),
        new Employee(104, "Monk Sha", 8, 7777.77),
        new Employee(105, "White dragon horse", 38, 5555.55)
    );
    public static void main(String[] args) {
        List<Employee> list = filterEmployee(emps, new FilterEmployeeForAge());//Interface callback
        for (Employee employee : list) {
            System.out.println(employee);
        }

        System.out.println("------------------------------------------");

        List<Employee> list2 = filterEmployee(emps, new FilterEmployeeForSalary());//Interface callback
        for (Employee employee : list2) {
            System.out.println(employee);
        }
    }

    public static List<Employee> filterEmployee(List<Employee> emps, ObjectDataPredicate<Employee> objectDataPredicate){
        List<Employee> list = new ArrayList<>();
        for (Employee employee : emps) {
            if(objectDataPredicate.test(employee)){
                list.add(employee);
            }
        }
        return list;
    }
}
Copy code

There are too many implementation classes of this code

D. Code evolution: anonymous inner classes

package com.os.test;

import com.os.model.Employee;
import com.os.service.FilterEmployeeForAge;
import com.os.service.FilterEmployeeForSalary;
import com.os.service.ObjectDataPredicate;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Demo03 {
    private static List<Employee> emps = Arrays.asList(
        new Employee(101, "name of a fictitious monkey with supernatural powers", 18, 9999.99),
        new Employee(102, "Bajie", 59, 6666.66),
        new Employee(103, "Tang Monk", 28, 3333.33),
        new Employee(104, "Monk Sha", 8, 7777.77),
        new Employee(105, "White dragon horse", 38, 5555.55)
    );
    public static void main(String[] args) {
        List<Employee> list = filterEmployee(emps, new ObjectDataPredicate<Employee>() {
            @Override
            public boolean test(Employee employee) {
                return employee.getId() <= 103;
            }
        });//Interface callback
        for (Employee employee : list) {
            System.out.println(employee);
        }
    }
    public static List<Employee> filterEmployee(List<Employee> emps, ObjectDataPredicate<Employee> objectDataPredicate){
        List<Employee> list = new ArrayList<>();
        for (Employee employee : emps) {
            if(objectDataPredicate.test(employee)){
                list.add(employee);
            }
        }
        return list;
    }
}
Copy code

Through the inner class, we can find that the core part of the whole code is one sentence, employee getId() <= 103

E. Code evolution: Lambda expressions

package com.os.test;

import com.os.model.Employee;
import com.os.service.ObjectDataPredicate;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Demo04 {
	private static List<Employee> emps = Arrays.asList(
			new Employee(101, "name of a fictitious monkey with supernatural powers", 18, 9999.99),
			new Employee(102, "Bajie", 59, 6666.66),
			new Employee(103, "Tang Monk", 28, 3333.33),
			new Employee(104, "Monk Sha", 8, 7777.77),
			new Employee(105, "White dragon horse", 38, 5555.55)
	);
	public static void main(String[] args) {
        /*
         List<Employee> list = filterEmployee(emps, new ObjectDataPredicate<Employee>() {
            @Override
            public boolean test(Employee employee) {
                return employee.getId() <= 103;
            }
        });//Interface callback
        */
		List<Employee> list = filterEmployee(emps, (e) -> e.getAge() <= 35);
		for (Employee employee : list) {
			System.out.println(employee);
		}

		System.out.println("------------------------------------------");

		List<Employee> list2 = filterEmployee(emps, (e) -> e.getSalary() >= 5000);
		for (Employee employee : list2) {
			System.out.println(employee);
		}
	}

	public static List<Employee> filterEmployee(List<Employee> emps, ObjectDataPredicate<Employee> objectDataPredicate){
		List<Employee> list = new ArrayList<>();
		for (Employee employee : emps) {
			if(objectDataPredicate.test(employee)){
				list.add(employee);
			}
		}
		return list;
	}
}

Copy code

Lambda is an anonymous function. We can understand lambda expression as a piece of code that can be passed (pass the code like data). You can write more concise and flexible code. As a more compact code style, the language expression ability of Java has been improved.

2, Lambda basic grammar

Lambda expressions introduce a new syntax element and operator into the Java language. This operator is - >, which is called lambda operator or scissor operator. It divides lambda into two parts:

  • Left: specifies all parameters required by Lambda expression (corresponding to formal parameters in the interface)
  • Right: Specifies the Lambda body, that is, the function to be performed by the Lambda expression. (method body, you can infer the return value type)

A. Format 1: no parameter, no return value

package com.os.print.service;
@FunctionalInterface//Define interface functions
public interface Printer01 {
	void print();
}
Copy code
package com.os.print.service;

public class Demo01 {
	public static void main(String[] args) {
		//We can use your implementation class before
		Printer01 out = new Printer01() {
			@Override
			public void print() {
				System.out.println("Anonymous implementation class");
				System.out.println("====>"+Math.random());
			}
		};
		out.print();
		//Using Lambda expressions
		out = ()-> System.out.println("The method body has only one line, and braces can be omitted");
		out.print();

		out = ()->{
			System.out.println("There are many method bodies, which need to be handled with curly braces");
			System.out.println("====>"+Math.random());
		};
		out.print();
	}
}
Copy code

B. Format 2: there is one parameter and no return value

package com.os.print.service;
@FunctionalInterface//Define interface functions
public interface Printer02<T> {
	void print(T t);
}
Copy code
public class Demo02 {
	public static void main(String[] args) {
		//Inferring the type of parameter e through generics
		Printer02<Employee> out01 = (e)-> System.out.println(e);
		out01.print(new Employee(999,"name of a fictitious monkey with supernatural powers",19,25000));

		Printer02<Integer> out2 = (e)-> System.out.println(e);
		out2.print(999);

		Printer02<String> out3 = (e)-> System.out.println(e);
		out3.print("Journey to the West");
	}
}
Copy code

C. Format 3: if there is only one parameter, parentheses can be omitted

package com.os.print.service;

import com.os.model.Employee;

public class Demo02 {
	public static void main(String[] args) {
		//Inferring the type of parameter e through generics
		Printer02<Employee> out01 = e-> System.out.println(e);
		out01.print(new Employee(999,"name of a fictitious monkey with supernatural powers",19,25000));

		Printer02<Integer> out2 = e-> System.out.println(e);
		out2.print(999);

		Printer02<String> out3 = e-> System.out.println(e);
		out3.print("Journey to the West");
	}
}
Copy code

D. Format 4: there are more than two parameters, return values, and multiple statements in the Lambda body

The function interface test of the system is as follows:

package com.os.print.service;

import com.os.model.Employee;
import java.util.Comparator;

public class Demo03 {
	public static void main(String[] args) {
		/*
		public interface Comparator<T> {
		}
		* */
		Comparator<Integer> comparator = (x,y)->{
			System.out.println("Interface function method");
			return Integer.compare(x,y);
		};
	}
}
Copy code

Custom function interface method:

package com.os.print.service;
@FunctionalInterface//Define interface functions
public interface Printer03<T> {
	T print(T t1,T t2);
}
Copy code
package com.os.print.service;

import com.os.model.Employee;

import java.util.Comparator;

public class Demo03 {
	public static void main(String[] args) {
		Printer03<String> out01 = (s1,s2)->{
			String str = s1.concat(s2);
			return str.toUpperCase();
		};
		System.out.println(out01.print("abc","efg"));
	}
}
Copy code

User defined function interface method has two parameters:

package com.os.print.service;
@FunctionalInterface//Define interface functions
public interface Printer04<T,R> {
	R print(T t1, R t2);
}
Copy code
package com.os.print.service;

import com.os.model.Employee;
public class Demo04 {
	public static void main(String[] args) {
		Printer04<String, Employee> out = (name,e)->{
			e.setName(name);
			return e;
		};
		Employee employee = out.print("Journey to the West",new Employee());
		System.out.println(employee);
	}
}
Copy code

E. Format 5: if there is only one statement in Lambda body, return and braces can be omitted

package com.os.print.service;

import com.os.model.Employee;

import java.util.Comparator;

public class Demo04 {
	public static void main(String[] args) {
		Comparator<Integer> comparator = (x, y)->Integer.compare(x,y);
        
		System.out.println(comparator.compare(1,2));
		Printer04<String, Employee> out = (name,e)->e;
		Employee employee = out.print("Journey to the West",new Employee());
		System.out.println(employee);
	}
}
Copy code

F. Format 5: the data type of the parameter list of Lambda expression can be omitted, because the JVM compiler infers the data type through the context, that is, "type inference"

(Integer x, Integer y) -> Integer.compare(x, y);  //This is not generally used
 Copy code

The parameter types in the above Lambda expressions are inferred by the compiler. There is no need to specify the type in Lambda expression, and the program can still be compiled. This is because javac infers the type of parameter in the background according to the context of the program. The type of Lambda expression depends on the context and is inferred by the compiler. This is called "type inference"

lambda syntax is summarized as follows:

  • Couplet above: a parenthesized province is encountered on the left and right
  • Second line: infer type province on the left
  • Banner: save if you can

3, Functional interface

  • An interface that contains only one abstract method is called a functional interface.

  • You can create the object of this interface through Lambda expression.

    • (if the Lambda expression throws a checked exception, the exception needs to be declared on the abstract method of the target interface).
  • Set @ FunctionalInterface annotation on any functional interface to check whether it is a functional interface. At the same time, javadoc will also contain a declaration that this interface is a functional interface.

In the above example, we have defined functional interfaces, but we can't define functional interfaces every time. It's too troublesome! Therefore, Java has built-in functional interfaces in Java util. Function package

A.Predicate assertion interface

@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
}    
Copy code
package com.os.print.service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

public class Demo05 {
	public static void main(String[] args) {
		List<String> list = Arrays.asList("Hello", "pangsir", "Lambda", "www", "ok");
		List<String> strList = filterStr(list, (s) -> s.length() > 3);
		for (String str : strList) {
			System.out.println(str);
		}
	}
	//Requirement: put the strings that meet the conditions into the collection
	public static List<String> filterStr(List<String> list, Predicate<String> pre){
		List<String> strList = new ArrayList<>();
		for (String str : list) {
			if(pre.test(str)){
				strList.add(str);
			}
		}
		return strList;
	}
}
Copy code

B. Function < T, R > functional interface

@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);
}    
Copy code
package com.os.print.service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;

public class Demo06 {
	public static void main(String[] args) {
		String newStr = strHandler("\t\t\t Journey to the West Monkey King   ", (str) -> str.trim());
		System.out.println(newStr);

		String subStr = strHandler("Journey to the West Monkey King", (str) -> str.substring(2, 5));
		System.out.println(subStr);
	}
	//Requirements: used to process strings
	public static String strHandler(String str, Function<String, String> fun){
		return fun.apply(str);
	}
}
Copy code

C.Supplier supply interface

@FunctionalInterface
public interface Supplier<T> {
    T get();
}
Copy code
package com.os.print.service;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;

public class Demo07 {
	public static void main(String[] args) {
		List<Integer> numList = getNumList(10, () -> (int)(Math.random() * 100));

		for (Integer num : numList) {
			System.out.println(num);
		}
	}
	//Requirement: generate a specified number of integers and put them into the set
	public static List<Integer> getNumList(int num, Supplier<Integer> sup){
		List<Integer> list = new ArrayList<>();

		for (int i = 0; i < num; i++) {
			Integer n = sup.get();
			list.add(n);
		}

		return list;
	}
}

Copy code

D.Consumer consumer interface

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
}    
Copy code
package com.os.print.service;
import java.util.function.Consumer;
public class Demo08 {
	public static void main(String[] args) {
		happy(10000, (m) -> System.out.println("Shopping consumption:" + m + "element"));
	}
	public static void happy(double money, Consumer<Double> con){
		con.accept(money);
	}
}
Copy code

java. util. There are many useful functional interfaces under the function package

Functional interfaceParameter typeReturn typepurpose
Consumer consumer interfaceTvoidApply operations to objects of type T, including methods: void accept (T)
Supplier supply interfacenothingTReturn an object of type T, including methods: T get();
Function < T, R > functional interfaceTRApply an action to an object of type T. The result is an object of type R. Methods: R apply(T t);
Predicate type interfaceTbooleanDetermines whether an object of type T satisfies a constraint. boolean value. Including method boolean test (T);
BiFunction<T,U,R>T,URThe type of operation is u, and the return result is t. The inclusion method is: R apply (T, T,U);
UnaryOperatorTTPerforms a unary operation on an object of type T and returns the result of type T. The inclusion method is T apply(Tt);
BinaryOperatorT,TTPerforms a binary operation on an object of type T and returns the result of type T. The inclusion method is T apply(Tt1,Tt2);
BiConsumer<T,U>T,UvoidApply operations to parameters of type T,U. The containing method is void accept (T, T,U)
ToIntFunctionToLongFunctionToDoubleFunctionTintlongdoubleFunctions that calculate the values of int, long, double and
IntFunctionLongFunctionDoubleFunctionintlongdoubleRThe parameters are functions of type int, long and double respectively

4, Method reference

When the operation to be passed to the Lambda body has an implemented method, you can use the method reference!

Method reference: use the operator "::" to separate the method name from the name of the object or class.

  • Object:: instance method
  • Class:: static method
  • Class:: instance method

be careful:

  • ① The parameter list and return value type of the method referenced by the method reference should be consistent with the parameter list and return value type of the abstract method in the functional interface! Copy code

  • ② If the first parameter in Lambda's parameter list is the caller of the instance method and the second parameter (or no parameter) is the parameter of the instance method, the format: ClassName::MethodName copy the code

A. Object reference:: instance method name

package com.os.print.service;

import com.os.model.Employee;
import java.util.function.Supplier;

public class Demo09 {
	public static void main(String[] args) {
		Employee emp = new Employee(101, "Zhang San", 18, 9999.99);

		Supplier<String> sup = () -> emp.getName();
		System.out.println(sup.get());

		System.out.println("----------------------------------");

		Supplier<String> sup2 = emp::getName;
		System.out.println(sup2.get());
	}
}
Copy code

B. Class:: static method name

package com.os.print.service;

import java.util.function.BiFunction;
import java.util.function.Supplier;
public class Demo10 {
	public static void main(String[] args) {
		BiFunction<Double, Double, Double> fun = (x, y) -> Math.max(x, y);
		System.out.println(fun.apply(1.5, 22.2));

		System.out.println("--------------------------------------------------");

		BiFunction<Double, Double, Double> fun2 = Math::max;
		System.out.println(fun2.apply(1.2, 1.5));
	}
}
Copy code

C. Class:: instance method name

package com.os.print.service;

import com.os.model.Employee;

import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Function;

public class Demo11 {
	public static void main(String[] args) {
		BiPredicate<String, String> bp = (x, y) -> x.equals(y);
		System.out.println(bp.test("abcde", "abcde"));

		System.out.println("-----------------------------------------");

		BiPredicate<String, String> bp2 = String::equals;
		System.out.println(bp2.test("abc", "abc"));

		System.out.println("-----------------------------------------");


		Function<Employee, String> fun = (e) -> e.getName();
		System.out.println(fun.apply(new Employee()));

		System.out.println("-----------------------------------------");

		Function<Employee, String> fun2 = Employee::getName;
		System.out.println(fun2.apply(new Employee()));
	}

}

Copy code

If the first parameter in Lambda's parameter list is the caller of the instance method and the second parameter (or no parameter) is the parameter of the instance method, the format is: ClassName::MethodName

D. Constructor reference ClassName::new

Format: ClassName::new is combined with functional interface and is automatically compatible with methods in functional interface. The constructor reference can be assigned to the defined method, and the constructor parameter list should be consistent with the parameter list of the abstract method in the interface!

package com.os.print.service;

import com.os.model.Employee;

import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Function;

public class Demo12 {
	public static void main(String[] args) {

		Function<String, Employee> fun = Employee::new;
		System.out.println(fun.apply("name of a fictitious monkey with supernatural powers"));

		BiFunction<String, Integer, Employee> fun2 = Employee::new;
		System.out.println(fun2.apply("Bajie",18));
	}

}
Copy code

E. Array reference

package com.os.print.service;

import com.os.model.Employee;

import java.util.function.BiFunction;
import java.util.function.Function;

public class Demo13 {
	public static void main(String[] args) {

		Function<Integer, String[]> fun = (e) -> new String[e];
		String[] strs = fun.apply(10);
		System.out.println(strs.length);

		System.out.println("--------------------------");

		Function<Integer, Employee[]> fun2 = Employee[] :: new;
		Employee[] emps = fun2.apply(20);
		System.out.println(emps.length);
	}

}

Pay attention to official account: Kirin reform bug Share 2021 gold three silver four Java Summary collection of interview questions!

Keywords: Java Programming Lambda list

Added by willeh_ on Mon, 31 Jan 2022 14:57:24 +0200