Java foundation lambda expression

Keywords: Java

1. Concept

  • lambda expressions are a new feature added in Java 8.
  • More generally, a lambda expression is like an anonymous method: it provides a list of formal parameters and the body of the statement represented by these parameters.
  • The evaluation of lambda expressions produces Functional interface Examples of. The evaluation of the expression will not trigger the execution of the expression body, which will only occur when an appropriate method of the functional interface is called.

2. Structure

  • For a method, the complete structure should include return type, method name, parameter list and method body.
  • Since lambda expressions are equivalent to anonymous functions, there is no need to specify the method name. And because lambda expressions can infer the return type from the context, there is no need to explicitly specify the return type.
  • Therefore, the basic structure of lambda expression only includes parameter list and method body, that is () - > {};
    • (): used to describe the parameter list.
    • ->: lambda operator, read as goes to.
    • {}: used to describe the method body.

3. Format example

  • No parameter, no return value
  • No parameter, return value
  • One parameter, no return value
  • A parameter with a return value
  • Multiple parameters, no return value
  • Multiple parameters with return value
//1. No parameter, no return value
interface LambdaNoneParamNoneRet {
    void test();
}

//2. No parameter, return value
interface LambdaNoneParamSingleRet {
    int test();
}

//3. One parameter, no return value
interface LambdaSingleParamNoneRet {
    void test(int a);
}

//4. A parameter with return value
interface LambdaSingleParamSingleRet {
    int test(int a);
}

//5. Multiple parameters, no return value
interface LambdaMultipleParamNoneRet {
    void test(int a, int b);
}

//6. Multiple parameters with return values
interface LambdaMultipleParamSingleRet {
    int test(int a, int b);
}

public class Demo {
    public static void main(String[] args) {
        LambdaNoneParamNoneRet lambda1 = () -> {
            System.out.println(10);
        };
        lambda1.test();

        LambdaNoneParamSingleRet lambda2 = () -> {
            return 10;
        };
        System.out.println(lambda2.test());

        LambdaSingleParamNoneRet lambda3 = (int num) -> {
            System.out.println(num);
        };
        lambda3.test(10);

        LambdaSingleParamSingleRet lambda4 = (int num) -> {
            return num;
        };
        System.out.println(lambda4.test(10));

        LambdaMultipleParamNoneRet lambda5 = (int num1, int num2) -> {
            System.out.println(num1 + num2);
        };
        lambda5.test(10, 10);

        LambdaMultipleParamSingleRet lambda6 = (int num1, int num2) -> {
            return num1 + num2;
        };
        System.out.println(lambda6.test(10, 10));
    }
}
/*[Output]
10
10
10
10
20
20
*/

4. Basic grammar

  • Type writing method in parameter list: ① declare type; ② Inferring types, that is, omitting type declarations, is derived from functional interface types targeted by lambda expressions.
  • lambda expressions whose formal parameters have declared types are called explicitly typed; lambda expressions with inferred formal parameters are called implicitly typed. lambda expressions with zero arguments are explicitly typed.
  • Parameter lists are not allowed to mix declared and inferred type parameters. That is, for a lambda expression, it is impossible to declare the types of some parameters, while leaving the types of other parameters to be inferred.
  • When using inferred types, modifiers (such as final) are not allowed, then final parameterName is an example of an error.
  • If there is only one parameter in the parameter list, the parentheses can be omitted.
  • If there is only one statement in the method body, braces can be omitted at this time. If the only statement is a return statement, you must omit the return keyword while omitting the braces.
public class Demo {
    public static void main(String[] args) {
        LambdaNoneParamNoneRet lambda1 = () -> System.out.println(10);
        lambda1.test();

        LambdaNoneParamSingleRet lambda2 = () -> 10;
        System.out.println(lambda2.test());

        LambdaSingleParamNoneRet lambda3 = num -> System.out.println(num);
        lambda3.test(10);

        LambdaSingleParamSingleRet lambda4 = num -> num;
        System.out.println(lambda4.test(10));

        LambdaMultipleParamNoneRet lambda5 = (num1, num2) -> System.out.println(num1 + num2);
        lambda5.test(10, 10);

        LambdaMultipleParamSingleRet lambda6 = (num1, num2) -> num1 + num2;
        System.out.println(lambda6.test(10, 10));
    }
}

5. Advanced syntax - Reference

5.1 method reference

  • You can quickly point a lambda expression to an implemented method. Its syntax is:
    • Object (object name):: instanceMethod -- this case is equivalent to a lambda expression passing parameters to the method. For example, System.out::println, the object is System.out, and the expression is equivalent to X - > System.out. Println (x).
    • Class (class name):: instanceMethod -- the first parameter of this form will become an implicit parameter of the method. For example, String::compareToIgnoreCase, the expression is equivalent to (x, y) - > x.comparetoignorecase (y).
    • Class (class name):: staticMethod - all parameters of the form are passed to the static method. For example, Math::pow, the expression is equivalent to (x, y) - > math.pow (x, y).
    • (supplementary) this::instanceMethod and super::instanceMethod -- this and super parameters can be used in method references, which are equivalent to this.instanceMethod and super.instanceMethod.
  • Note:
    • The parameter list must be consistent with the parameter list of the method defined in the interface.
    • The return value type must be consistent with the return value type of the method defined in the interface

5.2 constructor reference

  • Constructor references are similar to method references, except that the method name is new.
    • Class (class name):: new -- create a class object, such as String::new. The expression is equivalent to X - > new string (x).
    • dataType [] (type):: new -- create an array object, such as int []: new, the expression is equivalent to num - > New Int [num].

5.3 example

public class Demo extends DemoFather {
    public Demo() {
    }
//  	A test class can only have one parameterless constructor

    public void print(String str) {
        System.out.println("This " + str);
    }

    @Override
    public String toString() {
        return "Demo{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Test
    public void test() {
//        Anonymous Inner Class 
        Consumer<String> consumer1 = new Consumer<String>() {
            @Override
            public void accept(String str) {
                System.out.println(str);
            }
        };
        consumer1.accept("Anonymous Inner Class ");

//        lambda expressions 
        Consumer<String> consumer2 = x -> System.out.println(x);
        consumer2.accept("lambda expression");

//        Method reference - object::instanceMethod
        Consumer<String> consumer3 = System.out::println;
        //The expression is equivalent to X - > system. Out. Println (x)
        consumer3.accept("System.out::println");

//        Method reference - Class::instanceMethod
        BiFunction<String, String, Integer> biFunction1 = String::compareToIgnoreCase;
        //The expression is equivalent to (x, y) - > x.comparetoignorecase (y)
        int res1 = biFunction1.apply("A", "b");
        System.out.println(res1);

//        Method reference - Class::staticMethod
        BiFunction<Double, Double, Double> biFunction2 = Math::pow;
        double res2 = biFunction2.apply(2.0, 2.0);
        System.out.println(res2);

//        Method reference - this/super::staticMethod
        Consumer<String> consumer4 = this::print;
        consumer4.accept("method");

        Consumer<String> consumer5 = super::print;
        consumer5.accept("method");

//        Constructor reference
        Supplier<Demo> supplier1 = Demo::new;
        Demo d1 = supplier1.get();
        System.out.println(d1);

        BiFunction<String, Integer, DemoFather> supplier2 = DemoFather::new;
        DemoFather d2 = supplier2.apply("xiaoming", 10);
        System.out.println(d2);

        Function<Integer, int[]> function1 = int[]::new;
        int[] arr = function1.apply(10);
        System.out.println(Arrays.toString(arr));

    }

}

class DemoFather {
    public String name;
    public int age;

    public DemoFather() {
    }

    public void print(String str) {
        System.out.println("Super " + str);
    }

    public DemoFather(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "DemoFather{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
/*[Output]
Anonymous Inner Class 
lambda expression
System.out::println
-1
4.0
This method
Super method
Demo{name='null', age=0}
DemoFather{name='xiaoming', age=10}
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
 */

6. Closure

  • A closure is a block of code that contains free variables.
  • Actually, lambda expressions have three parts:
    • parameter
    • Code block
    • The value of a free variable, which is a non parametric variable that is no longer defined in the code block.
public class Demo{
    public String name = "Demo";

    public void test1(){
        int n = 10;
        Consumer<Integer> consumer = x ->{
//            n++; ERROR:Variable used in lambda expression should be final or effectively final
        };
    }

    public void test2(){
        int n = 10;
        Consumer<Integer> consumer = x ->{
//            System.out.println(n); ERROR:Variable used in lambda expression should be final or effectively final
        };
        n++;
    }

    public void test3(){
        int n = 10;
        Consumer<Integer> consumer = x ->{
//            int n = 2;  ERROR:Variable 'n' is already defined in the scope
        };
    }

    public void test4(){
        int n = 10;
//        Consumer<Integer> consumer = n ->{}; ERROR:Variable 'n' is already defined in the scope
    }

    @Test
    public void test5(){
        Consumer<String> consumer = x ->{
            System.out.println(x + this.toString());
        };
        consumer.accept("Hi ");
    }
    
    @Override
    public String toString() {
        return "Demo{" +
                "name='" + name + '\'' +
                '}';
    }
}
/*[Output]
Hi Demo{name='Demo'}
*/
  • It can be seen that lambda expressions can capture the values of variables in the peripheral scope, which must actually be effective final variables. In fact, the final variable means that it will not be assigned a new value after initialization.
  • be careful:
    • It is illegal to declare a parameter or local variable with the same name as a peripheral local variable in a lambda expression.
    • When this keyword is used in a lambda expression, it refers to the this parameter of the method that creates the lambda expression (for example, this in test5() refers to the instance object of Demo, not the instance object of consumer < string >).

Posted by jami3 on Sun, 28 Nov 2021 12:31:11 -0800