JavaSE -- Lambda expression

Posted by payney on Mon, 27 Dec 2021 05:36:30 +0100

Lambda expression

Introduction:
Lambda expressions, also known as closures, are the most important new feature that drives the release of Java 8.
Lambda allows functions to be used as parameters of a method (functions are passed into the method as parameters).
Using Lambda expressions can make the code more concise and compact.

Standard format:

Lambda omits the object-oriented rules and regulations, and the format consists of three parts:

  • Some parameters
  • An arrow
  • A piece of code

The standard format for Lambda expressions is:

(Parameter type parameter name) -> { Code statement }

Format Description:

  • The syntax in parentheses is consistent with the parameter list of traditional methods: leave blank if there is no parameter; Multiple parameters are separated by commas.
  • ->Is a newly introduced syntax format, which represents pointing action.
  • The syntax in braces is basically consistent with the requirements of traditional method body.

Comparison between anonymous inner class and lambda:

new Thread(new Runnable() {
			@Override
			public void run() {
				System.out.println("Multithreaded task execution!");
			}
}).start();

After careful analysis of the code, the Runnable interface has only one definition of the run method:

  • public abstract void run();

That is, a plan for doing things has been formulated (in fact, it is a method):

  • No parameter: the scheme can be executed without any conditions.
  • No return value: the scheme does not produce any results.
  • Code block (method): the specific implementation steps of the scheme.

The same semantics is embodied in Lambda grammar, which should be simpler:

() -> System.out.println("Multithreaded task execution!")
  • The first pair of parentheses are the parameters of the run method (none), which means that no conditions are required;
  • An arrow in the middle represents passing the previous parameter to the following code;
  • The following output statement is the business logic code.

Omit format:

Omission rule

On the basis of Lambda standard format, the rules for using ellipsis are as follows:

  1. The types of parameters in parentheses can be omitted;
  2. If there is only one parameter in the parenthesis, the parenthesis can be omitted;
  3. If there is only one statement in braces, you can omit the braces, return keyword and statement semicolon regardless of whether there is a return value or not.

Note: after mastering these omission rules, please review the multithreading cases at the beginning of this chapter accordingly.

It can be deduced and omitted

Lambda emphasizes "what to do" rather than "how to do", so any information that can be deduced from the context can be omitted. For example, the above example can also use lambda's ellipsis:

Runnable Interface simplification:
1. () -> System.out.println("Multithreaded task execution!")
Comparator Interface simplification:
2. Arrays.sort(array, (a, b) -> a.getAge() - b.getAge());

Prerequisites for Lambda

Lambda's syntax is very concise and completely free from the constraints of object-oriented complexity. However, there are several problems that need special attention when using:

  1. Lambda must have an interface, and there must be only one abstract method in the interface.
    Whether it is the built-in Runnable, Comparator interface or user-defined interface of JDK, Lambda can be used only when the abstract methods in the interface exist and are unique.
  2. Using Lambda must have context inference.
    That is, the parameter or local variable type of the method must be the interface type corresponding to Lambda, and Lambda can be used as an instance of the interface.

Note: an interface with only one abstract method is called "functional interface".

Lambda expression case:

Define an interface

interface Lambda {
    /**Multi parameter no return*/
    @FunctionalInterface
    interface NoReturnMultiParam {
        void method(int a, int b);
    }

    /**No parameter, no return value*/
    @FunctionalInterface
    interface NoReturnNoParam {
        void method();
    }

    /**A parameter is not returned*/
    @FunctionalInterface
    interface NoReturnOneParam {
        void method(int a);
    }

    /**Multiple parameters have return values*/
    @FunctionalInterface
    interface ReturnMultiParam {
        int method(int a, int b);
    }

    /*** Return without parameters*/
    @FunctionalInterface
    interface ReturnNoParam {
        int method();
    }

    /**A parameter has a return value*/
    @FunctionalInterface
    interface ReturnOneParam {
        int method(int a);
    }
}

Direct test

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

        //No parameters, no return
        Lambda.NoReturnNoParam noReturnNoParam = () -> {
            System.out.println("NoReturnNoParam");//NoReturnNoParam
        };
        noReturnNoParam.method();

        //A parameter is not returned
        Lambda.NoReturnOneParam noReturnOneParam = (int a) -> {
            System.out.println("NoReturnOneParam param:" + a);//NoReturnOneParam param:6
        };
        noReturnOneParam.method(6);

        //Multiple parameters are not returned
        Lambda.NoReturnMultiParam noReturnMultiParam = (int a, int b) -> {
            System.out.println("NoReturnMultiParam param:" + "{" + a +"," + + b +"}");//NoReturnMultiParam param:{6,8}
        };
        noReturnMultiParam.method(6, 8);

        //No parameter has a return value
        Lambda.ReturnNoParam returnNoParam = () -> {
            System.out.print("ReturnNoParam");//ReturnNoParamreturn:1
            return 1;
        };

        int res = returnNoParam.method();
        System.out.println("return:" + res);

        //A parameter has a return value
        Lambda.ReturnOneParam returnOneParam = (int a) -> {
            System.out.println("ReturnOneParam param:" + a);//ReturnOneParam param:6
            return 1;
        };

        int res2 = returnOneParam.method(6);
        System.out.println("return:" + res2);//return:1

        //Multiple parameters have return values
        Lambda.ReturnMultiParam returnMultiParam = (int a, int b) -> {
            System.out.println("ReturnMultiParam param:" + "{" + a + "," + b +"}");//ReturnMultiParam param:{6,8}
            return 1;
        };

        int res3 = returnMultiParam.method(6, 8);
        System.out.println("return:" + res3);//return:1
    }
}

Syntax simplification ↓

We can further simplify the code and write more elegant code by observing the following code.

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

        //1. Simplify parameter types. You can not write parameter types, but you must not write all parameters
        Lambda.NoReturnMultiParam lamdba1 = (a, b) -> {
            System.out.println("Simplified parameter type");
        };
        lamdba1.method(1, 2);

        //2. Simplify parameter parentheses. If there is only one parameter, parameter parentheses can be omitted
        Lambda.NoReturnOneParam lambda2 = a -> {
            System.out.println("Simplified parameter parentheses");
        };
        lambda2.method(1);

        //3. Simplify the method body braces. If the method bar has only one statement, you can win the method body braces
        Lambda.NoReturnNoParam lambda3 = () -> System.out.println("Simplified method body braces");
        lambda3.method();

        //4. If the method body has only one statement and is a return statement, the method body braces can be omitted
        Lambda.ReturnOneParam lambda4 = a -> a+3;
        System.out.println(lambda4.method(5));

        Lambda.ReturnMultiParam lambda5 = (a, b) -> a+b;
        System.out.println(lambda5.method(1, 1));
    }
}

Common examples of Lambda expressions
lambda expression reference method
Sometimes we don't have to rewrite the method of an anonymous inner class ourselves. We can use the interface of lambda expression to quickly point to an implemented method.

grammar

Method owner: method name. The owner of static method is the class name, and the owner of common method is the object

public class Exe1 {
    public static void main(String[] args) {
        ReturnOneParam lambda1 = a -> doubleNum(a);
        System.out.println(lambda1.method(3));

        //lambda2 refers to the already implemented doubleNum method
        ReturnOneParam lambda2 = Exe1::doubleNum;
        System.out.println(lambda2.method(3));

        Exe1 exe = new Exe1();

        //lambda4 refers to the already implemented addTwo method
        ReturnOneParam lambda4 = exe::addTwo;
        System.out.println(lambda4.method(2));
    }

    /**
     * requirement
     * 1.The number and type of parameters shall be consistent with those defined in the interface
     * 2.The return value type should be consistent with that defined in the interface
     */
    public static int doubleNum(int a) {
        return a * 2;
    }

    public int addTwo(int a) {
        return a + 2;
    }
}

Reference to construction method
In general, we need to declare the interface, which is an object generator, instantiate the object by way of class name: new, and then call the method to return the object.

interface ItemCreatorBlankConstruct {
    Item getItem();
}
interface ItemCreatorParamContruct {
    Item getItem(int id, String name, double price);
}

public class Exe2 {
    public static void main(String[] args) {
        ItemCreatorBlankConstruct creator = () -> new Item();
        Item item = creator.getItem();

        ItemCreatorBlankConstruct creator2 = Item::new;
        Item item2 = creator2.getItem();

        ItemCreatorParamContruct creator3 = Item::new;
        Item item3 = creator3.getItem(112, "mouse", 135.99);
    }
}

lambda expression creation thread
In the past, we used to create a Thread object, and then override the run() method through an anonymous inner class. When we mention an anonymous inner class, we should think of using lambda expressions to simplify the Thread creation process.

    Thread t = new Thread(() -> {
      for (int i = 0; i < 10; i++) {
        System.out.println(2 + ":" + i);
      }
    });
  	t.start();

Traversal set
We can call the public void foreach (Consumer <? Super E > action) method of the collection to traverse the elements in the collection through lambda expressions. The following are the methods of the Consumer interface and the operations of traversing the collection. The Consumer interface is a functional interface provided by jdk.

    @FunctionalInterface
    public interface Consumer<T> {
        void accept(T t);
        //....
    }
      ArrayList<Integer> list = new ArrayList<>();

      Collections.addAll(list, 1,2,3,4,5);

      //lambda expression method reference
      list.forEach(System.out::println);

      list.forEach(element -> {
        if (element % 2 == 0) {
          System.out.println(element);
        }
      });

Delete an element in the collection
We use the public Boolean removeif (predict <? Super E > filter) method to delete an element in the collection. Predict is also a functional interface provided by jdk, which can simplify the programming.

      ArrayList<Item> items = new ArrayList<>();
      items.add(new Item(11, "Small toothbrush", 12.05 ));
      items.add(new Item(5, "Japanese toilet cover", 999.05 ));
      items.add(new Item(7, "Gree air conditioner", 888.88 ));
      items.add(new Item(17, "soap", 2.00 ));
      items.add(new Item(9, "Refrigerator", 4200.00 ));

      items.removeIf(ele -> ele.getId() == 7);

      //Traverse through foreach to see if it has been deleted
      items.forEach(System.out::println);

Sorting of elements within a collection
In the past, if we wanted to sort the elements in the collection, we had to call the sort method and pass in the comparator anonymous inner class to override the compare method. Now we can use lambda expressions to simplify the code.

        ArrayList<Item> list = new ArrayList<>();
        list.add(new Item(13, "vest", 7.80));
        list.add(new Item(11, "Half sleeve", 37.80));
        list.add(new Item(14, "Windbreaker", 139.80));
        list.add(new Item(12, "Autumn pants", 55.33));

        /*
        list.sort(new Comparator<Item>() {
            @Override
            public int compare(Item o1, Item o2) {
                return o1.getId()  - o2.getId();
            }
        });
        */

        list.sort((o1, o2) -> o1.getId() - o2.getId());

        System.out.println(list);

Closure problems in Lambda expressions#
This problem also exists in anonymous inner classes. If we release the annotation, we will report an error and tell me that the num value is final and cannot be changed. Although we did not identify the num type as final here, the virtual opportunity helped us add the final modifier keyword during compilation.

import java.util.function.Consumer;
public class Main {
    public static void main(String[] args) {

        int num = 10;

        Consumer<String> consumer = ele -> {
            System.out.println(num);
        };

        //num = num + 2;
        consumer.accept("hello");
    }
}

Topics: Java Lambda Back-end JavaSE