New features in Java 8 Lambda expressions and method references

1, Lambda expression

Format of Lambda expression

For example: (O1, O2) - > integer. Compare (O1, O2);

  • "- >": lambda operator or arrow operator
  • "- > left": lambda formal parameter list (actually the parameter list of abstract methods in the interface)
  • "- > right": lambda body (in fact, it is the method body of the abstract method rewriting the interface)

Lambada expressions are essentially instances of interfaces (functional interfaces)

1.1 no reference writing method

Runnable rab1 = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello World");
    }
};
rab1.run();

/** When there is no parameter, an empty bracket () is required to the left of "- >" */
Runnable rab2 = () -> System.out.println("Hello World");
rab2.run();

1.2 writing method of one parameter

Consumer con1 = new Consumer() {
    @Override
    public void accept(Object o) {
        System.out.println(o);
    }
};
con1.accept("Hello World");

/** The parentheses () of the parameter list can be ignored when a parameter is selected */
Consumer con2 = string -> System.out.println(string);
con2.accept("Hello World");

1.3 multiple parameters

Comparator<Integer> com1 = new Comparator<Integer>() {
    @Override
    public int compare(Integer o1, Integer o2) {
        return Integer.compare(o1,o2);
    }
};
int compare1 = com1.compare(11,22);
System.out.println(compare1);

/** When there are multiple parameters, the parameters need to be wrapped in parentheses ()*/
Comparator<Integer> com2 = (o1,o2) -> Integer.compare(o1,o2);
int compare2 = com2.compare(11,22);
System.out.println(compare2);

1.4 the method body contains multiple execution statements

Comparator<Integer> com1 = new Comparator<Integer>() {
    @Override
    public int compare(Integer o1, Integer o2) {
        System.out.println(o1);
        System.out.println(o2);
        return Integer.compare(o1,o2);
    }
};
int compare1 = com1.compare(11,22);
System.out.println(compare1);

/** When the method body contains multiple statements, it needs to be wrapped with {} */
Comparator<Integer> com2 = (o1,o2) -> {
    System.out.println(o1);
    System.out.println(o2);
    return Integer.compare(o1,o2);
};
int compare2 = com2.compare(11,22);
System.out.println(compare2);

If there is only one execution statement in the Lambda body, "{}" and return are not required. If there are multiple execution statements, "{}" and return must both be required

2, Function interface

2.1 function interface definition

The interface containing only one user-defined abstract method is called a functional interface. It is a functional interface whether there is a @ FunctionalInterface annotation or not. You can use the annotation to check whether it is a functional interface. At the same time, javadoc will also contain a declaration indicating that this interface is a functional interface

@In the interface annotated with FunctionalInterface, only one non static and default function can be called a function interface. Otherwise, the annotation will report an error, but there can be methods of Object class in the interface

@FunctionalInterface
public interface Test {
    void test(String str);

    @Override
    boolean equals(Object object);

    @Override
    String toString();

    static void print(){
        System.out.println("");
    }

    default void show(){
        System.out.println("");
    }
}

The above code shows a functional interface that can override the public method of the Object class. There can be static and default methods, but there can only be one custom abstract method

2.2 use of functional interfaces

  • Define a functional interface with only one user-defined abstract method, but it can have public methods of Object class, not protected methods

  • Method of calling function interface in method

  • When an external method calls a method of this class, you can customize the function that implements the function interface. The format (function interface method parameters) - > {specific implementation}

2.3 built in four functional interfaces

  • Consumer: consumer interface

    void accept(T t);
    
  • Supplier: supply type interface

    T get();
    
  • Function < T, R >: functional interface

    R apply(T t);
    
  • Predicate: predicate interface

    boolean test(T t);
    

    The use of assertive interfaces to filter collection elements

    public void test(){
        List<String> list = Arrays.asList("lizhi","linan","zhuyuzhu");
        List<String> filterList = filterString(list,name -> name.contains("zh"));
        System.out.println(filterList);
    }
    
    /** Load the qualified elements into a new list and return */
    private List<String> filterString(List<String> list, Predicate<String> predicate){
        List<String> filterList = new ArrayList<>();
        for (String name: list) {
            if (predicate.test(name)){
                filterList.add(name);
            }
        }
    
        return filterList;
    
    }
    

In most scenarios, the four built-in functional interfaces can meet the development requirements. The java.util.function package also contains many other functional interfaces based on the expansion of these four functional interfaces

The above example of using functional interfaces can be directly implemented with consumer interfaces

Demo demo = new Demo();
demo.method("Hello World",(str)-> {
    System.out.println("this is functional interface");
    System.out.println(str);
});

public void method(String str, Consumer test){
    System.out.println("This is main method");
    test.accept(str);
}

Lambda expressions are implemented based on functional interfaces

3, Method reference and construction reference

When the operation to be passed to the Lambda body already has an implemented method, you can use the method reference. This means that the original operation of Lambda body is to execute the method of a class or object. At this time, Lambda body can be implemented by method reference. Method reference is actually the syntax sugar of Lambda expression, which is a higher-level expression

Requirement: the parameter list and return value type of the interface abstract method must be consistent with the parameter list and return value type of the method referenced by the method

/** format */
Class name/object :: method;  //Separate the class or object from the method name by "::" to express that the Lambda method body executes the method
//There are three main uses
 object :: Instance method name
  class :: Static method name
  class :: Instance method name  //In this case, the above requirements are no longer met

Static methods can only be called through classes, not through objects

3.1 method reference

3.1.1 no return value and consistent parameter list
/**
 *	Consumer Method: void accept (T)
 *	System.out Represents PrintStream
 * 	PrintStream Method: void println (T)
 */
//lambda expressions 
Consumer con1 = s -> System.out.println(s);
con1.accept("Hello World");
//Method reference
Consumer con2 = System.out :: println;
con2.accept("Hello World");
3.1.2 the return value types are consistent and there is no parameter list
/**
 *	Supplier Method: T get()
 *	User Method: String getName()
 */
//lambda expressions 
User user = new User(1,"lizhi","man","159");
Supplier sup1 = () -> user.getName();
System.out.println(sup1.get());

//Method reference
Supplier sup2 = user :: getName;
System.out.println(sup2.get());
3.1.3 there is a parameter list and the return value types are consistent
//The parameter list of the method referenced by the method is consistent with that of the compare abstract method
Comparator<Integer> com1 = (o1,o2) -> Integer.compare(o1,o2);
int result1 =  com1.compare(11,22);

Comparator<Integer> com2 = Integer :: compare;
int result2 = com2.compare(11,22);
//The return value corresponding to the apply method and the round method is consistent with the parameter list type
Function<Double,Long> function1 = d -> Math.round(d);
long result1 = function1.apply(0.5);

Function<Double,Long> function2 = Math :: round;
long result2 = function2.apply(0.5);
3.1.4 example method of type adjustment
/**
 *	Comparator Method: int compare(T t1,T t2)
 *	String Method: int CompareTo (T) is called by the instance
 */
Comparator<String> com1 = (c1,c2) -> c1.compareTo(c2);
int result1 = com1.compare("dag","aia");

Comparator<String> com2 = String :: compareTo;
int result2 = com2.compare("dag","aia");

This writing is difficult to understand. The Comparator's abstract method compare contains two String type parameters, while the compareTo method calling String has only one parameter. The first parameter in compare is the initiator of the call. At the same time, the first parameter is also an instance of String, so the instance method is called through class, In fact, the first parameter of Comparator abstract method is the instance of this class

/**
 *	BiPredicate Method: boolean test(T t1,U t2)
 *	String Method: boolean equals(T t) is called by the instance
 */
BiPredicate<String,String> bi1 = (b1,b2) -> b1.equals(b2);
bi1.test("lizhi","linan");

BiPredicate<String,String> bi2 = String :: equals;
bi2.test("lizhi","linan");

In the following example, the apply abstract method has a parameter, and the getName method has no parameters, but the getName method needs to have an instance that initiates the call, so the parameters of the apply method act as the User instance

/**
 *	Function Method: R apply(T t)
 *	User Method: String getName() is called by the instance
 */
User user = new User(1,"lizhi","man","159");

Function<User,String> fun1 = u -> u.getName();
String userName1 =  fun1.apply(user);

Function<User,String> fun2 = User :: getName;
String userName2 = fun2.apply(user);

3.2 construction reference

3.2.1 nonparametric structure
Supplier<User> sup1 = () -> new User();
User user1 = sup1.get();

Supplier<User> sup2 = User::new;
User user2 = sup2.get();
3.2.2 single parameter structure
//User has a constructor whose parameter is of type int. there is no need to specify the parameter type
Function<Integer,User> fun1 = id -> new User(id);
User user1 = fun1.apply(100);

Function<Integer,User> fun2 = User :: new;
User user2 = fun2.apply(200);
3.2.3 multi parameter constructor
BiFunction<Integer,String,User> bif1 = (id,name) -> new User(id,name);
User user1 = bif1.apply(100,"lizhi");

BiFunction<Integer,String,User> bif2 = User :: new;
User user2 = bif2.apply(200,"linan");

3.3 array reference

Function<Integer,String[]> fun1 = length -> new String[length];
String[] array1 = fun1.apply(5);

Function<Integer,String[]> fun2 = String[] :: new;
String[] array2 = fun2.apply(5);

Tags: Java Lambda

Posted on Tue, 14 Sep 2021 23:26:41 -0400 by tyrnt