Java lambda expression learning notes

1, Introduction to Lambda expressions

1.1 what is a Lamdba expression?

Lambda expression is a new feature added by Java 8. It can be considered that lambda is an anonymous function (similar to anonymous inner class) to return an object that implements an interface (this view is very important and runs through the whole use process of lambda expression).

1.2 why use Lambada expressions?

Using Lambda expressions is very concise compared with other interface implementations. (see the method code block CodeBlock-1 of three interface implementations for details)

1.3 lambda's requirements for interfaces?

Although Lambda expressions can simply implement some interfaces, not all interfaces can be implemented using Lambda expressions. It is required that only one abstract method must be implemented for interface definition (Note: there can be multiple or no specific methods).

In Java 8, a new feature is added to the interface: default, which provides a default abstract method, but Lambda has no special impact on it. The method can be expressed according to Lambda.

1.4 precautions:

@FunctionalInterface
This annotation is used to modify a functional interface, which means that there can only be one abstract method in the interface, otherwise the compiler will report an error.

We always need objects to implement interfaces. Lambda expressions help us simplify this process, and individual methods in objects will not be executed during the creation of interface objects. The call of construction method in lambda expression in Section 4.2 is more like a factory method returning a reference to an object. The factory method is not executed when creating an object implementing the interface.

1.5 reasons for delayed execution of lambda expressions

(the following is from Java core technology Volume I)
The focus of using lambda expressions is deferred execution. After all, if you want to execute the code immediately, you can execute it directly without wrapping it in a lambda expression. There are many reasons why you want to execute the code later, such as:

  • Running code in a separate thread
  • Run the code multiple times and in the appropriate place of the algorithm (for example, the comparison operation of sorting)
  • Execute code when something happens (e.g., click a button, data arrives, etc.)]
  • Run code only when necessary

1.6 different ways of interface implementation
  there are three ways to create objects that implement interfaces, as shown in CodeBlock-1 code, which are divided into:

  1. Use the interface to implement the class (the interface object points to the class object that has implemented the interface)
  2. Implementing interfaces using anonymous inner classes
  3. Use lambda expressions to implement interfaces
    CodeBlock-1: three different ways to implement the interface
public class Program {
    public static void main(String[] args) {
        /***
         * 1.Implementing classes using interfaces
         */

        Comparator comparator = new MyComparator();

        /**
         * 2.Implementing interfaces using anonymous inner classes
         */

        Comparator comparator1 = new Comparator() {
            @Override
            public int compare(int a, int b) {
                return a - b;
            }
        };

        /**
         * 3.Use lambda expressions to implement interfaces
         *
         */
        Comparator comparator2 = (a, b) -> a - b;


        /**
         * If there is no error in the test part, three - 1 are displayed
         */

        System.out.println(comparator.compare(1, 2));
        System.out.println(comparator1.compare(1, 2));
        System.out.println(comparator2.compare(1, 2));
    }
}


class MyComparator implements Comparator {
    @Override
    public int compare(int a, int b) {
        return a - b;
    }
}

@FunctionalInterface
interface Comparator {
    int compare(int a, int b);
}

2, Basic syntax of Lambda expressions
2.1 basic grammar precautions

Lambda expression is an anonymous function
Focus: parameter list method body
Parentheses (): = used to describe a parameter list (formal parameter)
Braces {} describe a method body
->: Lambda operator, read as goes to, used to split parameter list and method body

2.2 code examples
We use various forms of Lambda expressions, which are divided into ordinary methods with or without return value (construction methods will be discussed later), non parameter, one parameter and multi parameter methods. There are six methods in total. Therefore, multiple interfaces with different methods are defined. You can know the meaning of the interface from its naming method (if it is not in the same package, pay attention to import).
Codeblock-2: six different interface definitions:

//1. Multi parameter interface without return value
@FunctionalInterface
public interface LambdaNoneReturnMultipleParameter {
    void test(int a, int b);
}
//2. Parameter free interface without return value
@FunctionalInterface
public interface LambdaNoneReturnNoneParameter {
    void test();
}
//3. One parameter interface without return value
@FunctionalInterface
public interface LambdaNoneReturnSingleParameter {
    void test(int n );
}

//4. Multi parameter interface with return value
@FunctionalInterface
public interface LambdaSingleReturnMultipleParameter {
    int test(int a, int b);
}
//5. Parameterless interface with return value
@FunctionalInterface
public interface LambdaSingleReturnNoneParameter {
    int test();
}
//6. One parameter interface with return value
@FunctionalInterface
public interface LambdaSingleReturnSingleParameter {
    int test(int n);
}

Codeblock-3: Lambda expression application of 6 different interfaces:

/**
 * Lambda Basic syntax of expressions
 */
public class Syntax1 {
    public static void main(String[] args) {
        /**
         * 1.Example of Lambda expression with no parameters and no return
         */
        LambdaNoneReturnNoneParameter lambda1 = () -> {
            System.out.println("lambda1:" + "Hello World!");
        };
        lambda1.test();

        /**
         * 2.Example of Lambda expression with single parameter without return value
         */

        LambdaNoneReturnSingleParameter lambda2 = (int i) -> {
            System.out.println("lambda2:" + i);
        };
        lambda2.test(1024);


        /**
         * 3.Example of Lambda expression with multiple parameters without return value
         */
        LambdaNoneReturnMultipleParameter lambda3 = (int a, int b) ->
        {
            System.out.println("lambda3:" + (a + b));
        };
        lambda3.test(1000, 24);

        /**
         * 4.Use example of parameterless Lambda expression with return value
         */

        LambdaSingleReturnNoneParameter lambda4 = () -> {
            return 1024;
        };
        int res = lambda4.test();
        System.out.println("lambda4:" + res);

        /**
         * 5.With a return value, the Lambdad expression of a single parameter is used
         */

        LambdaSingleReturnSingleParameter lambda5 = (int a) -> {
            return a;
        };
        int res2 = lambda5.test(1024);
        System.out.println("lambda5:" + res2);

        /**
         * 6.Lambdad expressions with return values and multiple parameters are used
         */
        LambdaSingleReturnMultipleParameter lambda6 = (int a, int b) -> {
            int sum = a + b;
            return sum;
        };
        int res3 = lambda6.test(1000, 24);
        System.out.println("lambda6:" + res3);

    }

}

3, Lambda expression syntax reduction

1. Parameter type: from the basic syntax example of Lambda expression, we hardly see the advantages of Lambda syntax, especially the elegance and simplification of code brought by Lambda compared with anonymous internal classes. However, Lambda syntax provides a reasonable way to simplify code. We study the simplification of Lambda expression in Chapter 3.
2. Parameter parentheses: if there is only one parameter in the parameter list, the parentheses can be omitted

  1. Reduction of method braces:
    Similar to if and while statements, braces can be omitted if the statement block has only one statement

  2. return omitted:
    If there are only unique methods in the interface, only unique statements in the method, and return statements, if you want to omit them, you can only omit the braces and return together, not one of them, otherwise an error will be reported.

3.2 code examples
  the interface used here is still defined by CodeBlock-2 code block.

CodeBlock-4: Code cases of four ways of interface simplification:

/**
 * This class is used for syntax reduction of Lambda expression demonstration
 */
public class Syntax2 {
    /**
     * Parameter reduction
     * 1.Simplification of parameters
     * Since the parameter has been defined in the interface, the type of the parameter can be omitted in the Lambda expression
     * Note: if the type needs to be omitted, but all parameter types must be omitted, and an error will be reported in the omitted part
     * It is not advisable to omit parameter types in anonymous inner classes
     */

    LambdaNoneReturnMultipleParameter lambda1 = (a, b) -> {
        System.out.println(a + b);

    };
    /**
     * 2.Condensed parameter parentheses
     * If there is only one parameter in the parameter list (no more or less), the parentheses can be omitted
     * And the type of parameter can still be omitted
     */
    LambdaNoneReturnSingleParameter lambda2 = a -> {
        System.out.println(a);
    };

    /**
     * 3.Omitting method braces
     * Similar to if and while statements, if the statement block has only one statement, the braces can be omitted
     * The previous omission is still valid
     */
    LambdaNoneReturnSingleParameter lambda3 = a ->
            System.out.println(a);
    /**
     * 4.If the only method of the interface has only a unique return statement, you can omit braces, but you must omit return while omitting large
     */
    LambdaSingleReturnNoneParameter lambda4 = () -> 10;

}

4, Function reference

Proposal of method reference: if there is a case where we create multiple interface implementation objects, and their methods are the same, but if the methods need to be modified, the complexity of modification increases with the increase of the number of objects.
**Definition of method reference: * * quickly point the implementation of a Lambda expression to a written method
Method reference can be regarded as a special form of lambda expression, or syntax sugar. Generally, a method reference can only be used when a method already exists. If a method does not exist, only a lambda expression can be used.

We can use two ways to call other methods in the Lambda expression, the first is the general method call, the second is the method reference.

Syntax description of method reference:
  that is: "subordinate of method: method name". The subordinate of a method depends on the method, that is, the subordinate of a static method is a class, and the subordinate of a non static method is an object (the subordinate is not an interface, but a class or object that defines a reference method).

matters needing attention:
 1. The number and type of parameters of the referenced method must be consistent with the number of method parameters in the interface;
 2. The return value of the referenced method must be consistent with the return value of the method in the interface. The overall expression of method reference can return the implementation object of the functional interface, but the return type of the method it calls / references is never the interface instance object;
 3. There is no bracket "()" after the method name;
 4. Method reference can have multiple parameter entries. Although it is not reflected in the re:: expression (because there are no parentheses), it has been specified in the interface;

4.1 calling of common methods in Lambda expressions

Codeblock-5: example description of two different common method calls:

public class Syntax3 {
    public static void main(String[] args) {
/**
 *Method reference: you can quickly point the implementation of a Lambda expression to a written method
 *Syntax: the subordinate of a method, the subordinate of a static method is a class, and the subordinate of a non static method is an object
 * Namely: "method subordinate: method name"
 * matters needing attention:
 * 1.The number and type of parameters of the referenced method must be consistent with the number of method parameters in the interface
 * 2.The return value of the referenced method must be consistent with the return value of the method in the interface
 *
 *
 * If we need to call an interface method many times in the program, it is not good to create an object with the following method to call the method
 * Disadvantages: if you want to change the method in the future, all objects defined with Lambda expressions will have to be changed, which is problematic in the design pattern;
 *      */
        LambdaSingleReturnSingleParameter lambda1 = a -> a * 2;
        LambdaSingleReturnSingleParameter lambda2 = a -> a * 2;

        /**
         * We usually write a general method and reference it to Lambda expressions
         *
         * */

        LambdaSingleReturnSingleParameter lambda3 = a -> change(a);//Use the call mode of general methods in Lambda expressions
        LambdaSingleReturnSingleParameter lambda4 = Syntax3::change;//Use method references in Lambda expressions (methods belong to classes)
		System.out.println(lambda4.test(2));
		Syntax3 syntax3 = new Syntax3();//Non static methods require objects to be called
        LambdaSingleReturnSingleParameter lambda5 = syntax3::change2;//Use method references in Lambda expressions (methods belong to objects)
		LambdaSingleReturnMultipleParameter lambda6 = syntax3::change3;//Multi parameter reference method usage
    }

    private static int change(int a) {
        return a * 2;
    }

    private int change2(int a) {
        return a * 2;
    }
}
    private int change3(int a, int b) {
        return a * 2 + b * 3;
    }

4.2 call of construction method in Lambda expression
  the Person class has nonparametric and parametric constructors.
CodeBlock-6: define a class and construct the object created by the method

public class Person {
    public String name;
    public int age;

    public Person() {
        System.out.println("Person The parameterless constructor of the class executes");//Statement is used to determine whether the parameterless constructor has been executed
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("The parameterized construction method of the method is executed");//Statement is used to determine whether a parameterized constructor has been executed
    }
}

Codeblock-7: example of reference construction method in lambda expression:

public class Syntax4 {

    public static void main(String[] args) {

        PersonCreater person = () -> new Person();

        /**Reference to construction method
         * The difference between the calls of the a parameterless constructor and a parameterless constructor lies in the parameters of the the constructor in the defined interface
         */

        PersonCreater creater1 = Person::new;
        //No parameter
        Person person1 = creater1.getPerson();
        //Have reference
        PersonCreater2 creater2=Person::new;
        Person person2 = creater2.getPerson("Fisherman",18 );

    }

}

//Requirement: an interface that returns a Person class

interface PersonCreater {
    Person getPerson();

}

interface PersonCreater2 {
    Person getPerson(String name, int age);

}

Array constructor reference:
Syntax format: typename []: new is equivalent to lambda expression: X - > New Int [x]

IntFunction<int[]> arrayMaker = int[]::new;//Suppose you have a functional interface that returns an array of int types
int[] array = arrayMaker.apply(10) // Create array int[10]

matters needing attention:

In the Lambda expression, an interface either corresponds to a parameterless constructor or contains a parameterless constructor. The method defined in the interface to abstract return the Person object has decided whether it has parameters or no parameters.
Constructors, like static methods, are methods belonging to classes
The constructor is different from the general static method "call like class name:: method name", but uses "class name:: new" to call the constructor.
The new keyword is used to explicitly know that the constructor is being called. The reason why it does not need entry parameters is that the compiler can infer the type and number of parameters through the definition of the interface. There is no essential difference between the method reference of the construction method and the ordinary method reference. For example, in CodeBlock-5, change(a) is used to implement the test method for returning the shaping data defined in the original interface, while the new keyword uses the constructor of the corresponding formal parameter to implement the getPerson method for returning the Person object defined in the interface.
:: in IDE (such as Intllij IDEA), always point to the functional interface implemented by the current method reference, so as to easily determine which functional interface is implemented by the method reference.

4.3 format summary of method reference:

Type of reference method Format specification Method issuer Equivalent lambda expression
Reference static method ClassName::staticMethodName class (s) -> String.valueOf(s)
An instance (non static) method that references an object ObjectName::instanceMethodName Current object ObjectName.instanceMethodName
An instance method that references any object of a certain type ClassName::methodName Objects of any class or subclass (any object, s) - > methodname (any object, s)
Reference construction method ClassName::new class (s) -> new ClassName(s);
The above s stands for formal parameters, which is limited to space. Only one is written symbolically. The number can not be 1, 0, 2, 3.

4.4 comparison between method reference and Lambda expression:

Method reference is more concise than Lambda expression, but it is also more difficult to understand its syntax, so we use the following comparison method to understand the expression.

4.4.1 static method reference

Composition syntax format: ClassName::staticMethodName

  • Static method reference is easy to understand, and compared with the lambda expression of static method call, it just takes Replace with:
  • Static method references can be used wherever the target type is compatible. At this point, the class is the initiator of the static method action.
    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);

4.4.2 method reference of specific instance object
Instance method references can be divided into the following three types:

Instance method reference on instance
This syntax is similar to that used for static methods, except that object references are used instead of class names. At this point, the object is the initiator of the method action.
Syntax format: instanceReference::methodName
example:
instanceReference::methodName is equivalent to (no or with parameters) - > instancereference Methodname (same number of parameters)
Since the object needs to be constructed, a code example is given below.

public class Test {
   public static void main(String[] args) {
   
       Power powerObject = new Power();
       Function<Integer,Integer> function1 = a->powerObject.power(a);
       Function<Integer,Integer> function2 = powerObject::power;
   	/**
   	*No matter which implementation, the method call is the same, and it is called with the implemented abstract method name of the interface.
   	*/
       System.out.println(function1.apply(2));
       System.out.println(function2.apply(3));
   }

}

class Power {
   public int power (int a ){
       return a*a;
   }
}

Instance method reference on superclass
Syntax format: super::methodName
The name of the method is specified by methodName. By using super, you can reference the superclass version of the method.
example:
You can also capture this pointer. This:: equals is equivalent to lambda expression X - > this equals(x);

Instance method reference on class (method reference of any object of a specific class)
Syntax format: ClassName::methodName
This is different from class calling static methods and object calling instances. The former class is no longer the sender of instance methods. Who is the sender? With this method, we can't find out who is the initiator of the action? In fact, the real initiator is any object created by the ClassName lock. However, when calling a method, you need to input the reference object as a parameter into the method, and it is specified that this object must be in the first of the method parameters.
Code case:

be careful:
  if the instance method of a type is generic, you need to provide a type parameter before the:: delimiter, or (in most cases) derive its type from the target type without adding a type parameter. However, you should understand its purpose if you use forced type conversion before encountering the delimiter.

T means that the return value is a generic type, and the type of data will be returned as soon as it is passed. A separate t means to limit the parameter type you pass. In this case, the first data in each set is obtained through a generic return method, which is realized by two methods of return value T and t

example:
  String::toString is equivalent to lambda expression (s) - > s.tostring(), but never equal to string Tostring(), because non static methods only allow object calls, not classes. But even if you say so, you may still not understand. I will give the following simple and easy to understand examples, and you will be able to master it.

We define a method to calculate the square, and the input parameter is "several" powers. For different implementations of the Power interface, we implement the provisions on the base of the Power. For example, PowerOfTwo class implements the power(i) method to find the i-Power of 2, and PowerOfThree class implements the power(i) method to find the i-Power of 3.
Class:: CodeBlock-1 of instance method:

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

public interface Power {
    int power(int i);
}

class PowerOfTwo implements Power {
    public int power(int i) {
        return (int) Math.pow(2, i);

    }
}

class PowerOfThree implements Power {
    public int power(int i) {
        return (int) Math.pow(3, i);

    }
}


class Test {
    public static void main(String[] args) {
        /**
         * BiFunction is used as a functional interface because it has two input parameters and one return value.
         * Just in line with the class name in this example Call rules for instance methods
         */
        Power powerObject1 = new PowerOfTwo();
        Power powerObject2 = new PowerOfThree();

        BiFunction<Power, Integer, Integer> function = Power::power;
        
        System.out.println(function.apply(powerObject1, 4));//Output "2" to the 4th power: 16
        System.out.println(function.apply(powerObject2, 4));//Output "3" to the 4th power: 81


    }

}

Only one implementation class of BiFunction interface is needed to realize the polymorphism of method call (the same method name has different operations due to different object names). Here, polymorphism is also formed because the parent class interface points to different subclass object implementations.

We should set bifunction < power, integer, integer > function = Power:: power; And function The two steps of apply (powerobject 1, 4) are combined, not isolated. The former specifies the input parameter type and return value type of the method, and specifies that the first parameter of the method must be the object actually calling the method, and implements the apply method of the latter (code implementation, not executed). The latter inputs the corresponding parameters and executes the relevant programs.

If you ask me why I need such a method reference form, the biggest reason is that the object is input into the method call as a parameter during the specific method call, which increases the convenience of polymorphism in the method reference, as shown in the above example. If other method reference methods are adopted, multiple interface instances will be generated, and this method only requires one interface instance.

The second example of a reference to an instance method on a class:
Class:: CodeBlock-2 of instance method:

public class LowercaseToUppercase {

    public static void main(String[] args) {

        List<String> list = Arrays.asList("hello", "world", "hello world");
        
        list.stream().map(String::toUpperCase).forEach(System.out::println);
    }
}

If you haven't learned flow, it doesn't affect the understanding here. From Crtl + left click +::, we can see that the map class needs to implement a Function interface, that is, an input and an output. However, as a method reference, the return value of String::toUpperCase is certain, because the toUpperCase method will obviously return a String type object: an uppercase String object; But what is the input parameter? It seems difficult to judge, because the toUpperCase method is a Function without parameters. But the JVM model tells us that the first parameter of the instance method of all instances is an implicit this. Map method, the elements of the stream are mapped (lowercase to uppercase), and the implicit parameter this in the method is each object.

Explain step by step in combination with JDK source code:

  1. map(String::toUpperCase), which passes an interface implementation object formed by a method reference into the map method
  2. Stream map(Function<? super T, ? extends R> mapper);, Map is a return stream object. The input is the object that implements the function interface, that is, the method reference in 1
  3. R apply(T t); This is the method to be implemented by the Function interface, that is, use the toUpperCase() method to implement it.
  4. public String toUpperCase() { return toUpperCase(Locale.getDefault());}, This is the source code of touppercase () method, which is a non static method that returns string class objects without parameters.
  5. The formal parameter t of the method to be implemented by the interface corresponds to any String object.

If you have doubts about "why the first parameter of the method in the application method call in example 1 is the reference of the operation object, but the reference of the object has never been entered in the second example", then you understand the key position. In fact, the latter is that the map calls the Function interface object and returns it to the stream. Finally, the apply method is called by the iterative method in the stream. Finally, the apply method is called with the object reference as the first input parameter. Therefore, the two examples achieve the class name:: instance method in the same way, but one is explicit and the other is implicit.

4.4.3 construction method reference

See subsection 4.2.

  1. summary
    Conclusion: the purpose of Lambda expression and method reference is to replace the abstract method in the interface with specific methods. But in practical use, it calls the implemented method name in the interface, the lambda expression and the method to refer to the construction process that is only applied to the interface instance, for example, the code in CodeBlock-6/7:
//1. This is an abstract method that needs to be implemented. Method name: getPerson
interface PersonCreater {
    Person getPerson();

}

//2. This is the object that implements the abstract method by using the method reference (returns an instance of the interface that implements the abstract method)
    PersonCreater creater1 = Person::new;

//3. This is the implementation method of calling the interface instance and returning a Person object, which is divided into with and without parameters.
	Person person1 = creater1.getPerson();

6. Common functional interfaces

Reference source:( https://blog.csdn.net/li_xunhuan/article/details/97930596 ), video source:( https://www.bilibili.com/video/BV1sb411j7Ms?from=search&seid=8494363257934162426&spm_id_from=333.337.0.0 )

Added by phillfox on Thu, 06 Jan 2022 01:51:26 +0200