Detailed explanation of the use of Lambda expressions

Table of contents

1. New ideas

1.1 Functional programming ideas

1.2. Functional interface

2. The road to lambdas

2.1. What is a lambda expression?

2.2. What are the characteristics of lambda expressions?

2.3.lambda expression usage scenarios

2.4.lambda expression syntax

2.5. Simplified writing of Lambda

2.6.Lambda expression structure

3. Stream

3.1 Overview

3.2. Stream operation

3.2, the use of stream

3.2.1, intermediate operation - filter

3.2.2, intermediate operation - map

4. Optional class depth analysis

4.1.Methods contained in the Optional class

4.2. Some examples

 5. Reference


1. New ideas

1.1 Functional programming ideas

        Object-oriented thinking needs to focus on what objects are used to accomplish what things. The idea of ​​functional programming is similar to the function in our mathematics. It is mainly concerned with what operations are performed on the data
.

1.2. Functional interface

        Functional interface (functional interface is also called functional interface, which is actually the same thing). In simple terms, a functional interface is an interface that contains only one method. For example, java.lang.Runnable and java.util.Comparator in the Java standard library are typical functional interfaces. Java 8 provides @FunctionalInterface as an annotation. This annotation is not necessary. As long as the interface conforms to the standard of functional interface (that is, the interface containing only one method), the virtual machine will automatically judge, but it is best to use the annotation @FunctionalInterface to declare on the interface. This prevents other members of the team from adding new methods to the interface by mistake.

The lambda in Java cannot appear alone, it needs a functional interface to flourish, and the method body of the lambda expression is actually the realization of the functional interface. The grammar will be mentioned below

2. The road to lambdas

2.1. What is a lambda expression?

        Lambda expression is a new feature added by JDK 1.8 . Its main purpose is to simplify the code and also make it more readable .

2.2. What are the characteristics of lambda expressions?

A lambda expression is an anonymous function with the following characteristics:

  • Concise : Using lambda can omit the function name and return keyword, making the code more concise.
  • Flexible: lambda can be passed as a parameter to other functions, and can also be nested in other functions.
  • One-off: lambda expressions are usually only used for one-off simple functions, not for complex function logic.

It should be noted that lambda is not the only way to implement anonymous functions in Python, there are some other ways, such as using the partial function in the functools module

2.3.lambda expression usage scenarios

Lambda expressions can be used in many scenarios, for example:

  • Functional programming: lambdas can be used to define anonymous functions, which is very common in functional programming.
  • Sorting: lambda can be used as a parameter of the sorting function to specify the sorting rules.
  • Filtering: lambda can be used as a parameter of the filter function to specify filter conditions.
  • Mapping: lambda can be used as a parameter of the mapping function to specify the mapping rules.
  • GUI programming: lambdas can be used to bind event handlers to simplify code.

In general, lambda expression is a very flexible syntax structure that can be used in many scenarios.

2.4.lambda expression syntax

Basic syntax:
(parameters) -> expression
or
(parameters) ->{ statements; }

Contains three parts

  1. A comma-separated formal parameter in parentheses, the parameter is the parameter of the method in the functional interface
  2. An arrow symbol: ->
  3. The method body can be an expression or a code block. The implementation of the method in the functional interface of the method body, if it is a code block, must be wrapped with {}, and a return return value is required, but there is an exception, if the function type The return value of the method in the interface is void, so there is no need for {}

- The syntax in parentheses is consistent with the parameter list of traditional methods. If there is no parameter, leave it blank, and if there are multiple parameters, separate them with commas

- [->] is a newly introduced grammatical format, representing a pointing action

   - The syntax inside curly braces is consistent with traditional method body requirements

// 1. 不需要参数,返回值为 5  
() -> 5  
  
// 2. 接收一个参数(数字类型),返回其2倍的值  
x -> 2 * x  
  
// 3. 接受2个参数(数字),并返回他们的差值  
(x, y) -> x – y  
  
// 4. 接收2个int型整数,返回他们的和  
(int x, int y) -> x + y  
  
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)  
(String s) -> System.out.print(s) 

Look at a complete example for easy understanding

/**
 * 测试lambda表达式
 *
 * @author benhail
 */
public class TestLambda {

    public static void runThreadUseLambda() {
        //Runnable是一个函数接口,只包含了有个无参数的,返回void的run方法;
        //所以lambda表达式左边没有参数,右边也没有return,只是单纯的打印一句话
        new Thread(() ->System.out.println("lambda实现的线程")).start(); 
    }

    public static void runThreadUseInnerClass() {
        //这种方式就不多讲了,以前旧版本比较常见的做法
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("内部类实现的线程");
            }
        }).start();
    }

    public static void main(String[] args) {
        TestLambda.runThreadUseLambda();
        TestLambda.runThreadUseInnerClass();
    }
}

It can be seen that the code designed using lambda expressions will be more concise and readable.

2.5. Simplified writing of Lambda

The prerequisite for using Lambda expressions: only the anonymous inner classes of functional interfaces (interfaces with one and only one abstract method) can be simplified;
Lambda expressions can be deduced and can be omitted. Lambda expressions can be deduced, and omitting is Rewrite the only abstract method;
Lambda expression can be deduced, the parameters and return value of the only abstract method, we can further simplify the parameters and return value;

Among them , the parameter list writes the parameters that need to be passed in the rewritten method . If there is no need to pass parameters, it can be left blank. The arrow indicates that the previous parameters are passed to the subsequent method body.

In fact, the core principle of Lambda expressions is: as long as it can be deduced, it can be omitted, that is, the content that can be deduced according to the context can be omitted. The content that can be omitted is as follows:

Simplification:
1. (parameter list): The data type in the parameter list can be omitted
(int a)==>(a)(int a,double d)==>(a,d)
2, (parameter list) : There is only one parameter in the parameter list, so parentheses () can also be omitted, but if there is no parameter, parentheses must be written
(inta)==>(a)==>a ()==>()
3 .{Rewrite the method body of the abstract method}: Regardless of whether the method has a return value, if the method body has only one line of code, omit return and {} and the semicolon at the end of this line of code (if you want to omit, you must omit all three together);

2.5.1. Omission rules

  • Parameter type can be omitted
  • When the method body has only one line of code, the brace return and the semicolon of the only line of code can be omitted
  • When the method has only one parameter, the parentheses can be omitted

2.5.2. Format description:

                - The syntax in parentheses is consistent with the parameter list of traditional methods. If there is no parameter, leave it blank, and if there are multiple parameters, separate them with commas

                - [->] is a newly introduced grammatical format, representing a pointing action

                - The syntax inside curly braces is consistent with traditional method body requirements
 

2.6.Lambda expression structure

  • A Lambda expression can have zero or more parameters
  • The types of parameters can be either explicitly declared or inferred from the context. For example: same effect (int a)as(a)
  • All parameters must be enclosed in parentheses and separated by commas. For example: (a, b) or  (int a, int b) or (String a, int b, float c)
  • Empty parentheses indicate an empty parameter set. For example:() -> 42
  • When there is only one parameter and its type can be deduced, the parentheses () can be omitted. For example:a -> return a*a
  • The body of a lambda expression can contain zero or more statements
  • If the body of the Lambda expression has only one statement, the curly braces {} can be omitted. The return type of the anonymous function is consistent with the body expression
  • If the body of a lambda expression contains more than one statement, the expression must be enclosed in curly braces {} (forming a code block). The return type of the anonymous function is consistent with the return type of the code block, and it is empty if there is no return

3. Stream

        Stream operation is an important new feature provided by Java 8, which allows developers to process collections in a declarative manner. Its core class library mainly improves the API for collection classes and adds Stream operations. Each method in the Stream class corresponds to an operation on the collection. Introducing real functional programming into Java can make the code more concise, greatly simplify the processing operations of collections, and improve the efficiency and productivity of development.

3.1 Overview

Java8's Stream uses a functional programming model, and like its name, it can be used to perform chained streaming operations on collections or arrays. It is more convenient for us
to operate on collections or arrays.

3.2. Stream operations

Each Stream has two modes: sequential execution and parallel execution.

sequence flow:

List <Person> people = list.getStream.collect(Collectors.toList());

parallel stream:

List <Person> people = list.getStream.parallel().collect(Collectors.toList());

As the name implies, when using the sequential method to traverse, each item is read before reading the next item. When using parallel traversal, the array is divided into multiple segments, each of which is processed in a different thread, and the results are output together.

Common methods in Stream are as follows:

1) Stream<T> filter​(Predicate predicate): used to filter the data in the stream

    The method boolean test​(T t) in the Predicate interface: judge the given parameters and return a Boolean value

2) Stream<T> limit​(long maxSize): returns the stream composed of elements in this stream, and intercepts the data with the specified number of parameters before

3) Stream<T> skip​(long n): Skip the data of the specified number of parameters and return a stream composed of the remaining elements of the stream

4) static <T> Stream<T> concat​(Stream a, Stream b): Merge the two streams a and b into one stream

5) Stream<T> distinct​(): Returns a stream composed of different elements of the stream (according to Object.equals(Object))

6) Stream<T> sorted​(): Returns a stream composed of elements of this stream, sorted according to natural order

7) Stream<T> sorted​(Comparator comparator): Returns a stream composed of elements of the stream, sorted according to the provided Comparator

8) <R> Stream<R> map​(Function mapper): Returns a stream composed of the results of a given function applied to the elements of this stream. Method R apply​(T t) in the Function interface

9) IntStream mapToInt​(ToIntFunction mapper): Returns an IntStream containing the result of applying the given function to the elements of this stream

        IntStream: represents the original int stream

        Method int applyAsInt​(T value) in ToIntFunction interface


3.2, the use of stream

    Stream流接口中定义了许多对于集合的操作方法,总的来说可以分为两大类:中间操作和终端操作:
  1. Intermediate operation: A stream will be returned. In this way, multiple intermediate operations can be connected to form a call chain and converted to another stream. Intermediate operations do not perform any result processing on the stream unless there is a terminal operation following the call chain.
  2. Terminal operation: a specific result will be returned, such as boolean, list, integer, etc.

Data preparation is as follows:

public class Employee {
    //姓名
    private String name;
    //年龄
    private Integer age;
    //性别
    private String sex;
    //薪资
    private Double salary;
    //住址
    private String address;
    //工作地
    private String baseAddress;
    //职位
    private String position;
    //主管
    private Employee employee;
    public Employee(String name, Integer age, String sex, Double salary, String address, String baseAddress, String position, Employee employee) {
        this.name = name;
        this.age = age;
        this.sex = sex;
        this.salary = salary;
        this.address = address;
        this.baseAddress = baseAddress;
        this.position = position;
        this.employee = employee;
    }
    @Override
    public String toString() {
        return JSON.toJSONString(this);
    }
//创建原始数据       
List<Employee> list = new ArrayList<>();
Employee ceo = new Employee("张三", 56, "男", 50000.42D, "浙江杭州", "浙江杭州", "ceo", null);
Employee manager1 = new Employee("李四", 47, "女", 20000.7D, "浙江宁波", "浙江宁波", "经理", ceo);
Employee manager2 = new Employee("王五", 45, "男", 24000.5D, "浙江金华", "浙江金华", "经理", ceo);
Employee employee1 = new Employee("麻六", 27, "女", 7000.6D, "浙江宁波", "广东广州", "售前", manager1);
Employee employee2 = new Employee("孙七", 28, "男", 8000.8D, "浙江宁波", "广东深圳", "售后", manager1);
Employee employee3 = new Employee("赵八", 27, "女", 9500.2D, "浙江杭州", "云南昆明", "售前", manager2);
Employee employee4 = new Employee("钱九", 26, "男", 9000.0D, "浙江杭州", "云南玉溪", "售后", manager2);
list.add(ceo);
list.add(manager1);
list.add(manager2);
list.add(employee1);
list.add(employee2);
list.add(employee3);
list.add(employee4);

3.2.1, intermediate operation - filter

public class StreamTest {
    public static void main(String[] args) {
        //查找名字为张三的员工信息,并打印出来
        list.stream().filter(employee -> StringUtils.equals("张三", employee.getName())).forEach(employee -> System.out.println(employee.getName()));
    }
}

result:

Zhang San

filter returns a value of type Boolean , true is reserved, false is discarded.

3.2.2, intermediate operation - map

//收集公司里所有员工的名字,并打印出来
list.stream().map(employee -> {
return employee.getName(); 
}).forEach(name-> System.out.println(name));

result:

Zhang
San Li Si
Wang Wu
Ma Liu
Sun Qi
Zhao Ba
Qian Jiu

4. Optional class depth analysis

        Calling a method gets the return value but cannot directly use the return value as a parameter to call other methods. We must first determine whether the return value is null, and only if it is not empty can it be used as a parameter of other methods. This is exactly what some Guava-like external APIs try to solve. Some JVM programming languages ​​such as Scala, Ceylon, etc. have already solved this problem in the core API. In my previous article, I described how Scala solves this problem.

This is a nullable container object. The isPresent() method returns true if the value exists, and calling the get() method returns the object.

4.1.Methods contained in the Optional class

4.1.1 of

Create an Optional for non-null values.

The of method creates the Optional class through the factory method. It should be noted that the parameter passed in when creating an object cannot be null. If the incoming parameter is null, NullPointerException is thrown.

//调用工厂方法创建Optional实例
Optional<String> name = Optional.of("Sanaulla");
//传入参数为null,抛出NullPointerException.
Optional<String> someNull = Optional.of(null);

4.1.2.ofNullable

Creates an Optional for the specified value, or returns an empty Optional if the specified value is null.

ofNullable is similar to the of method, the only difference is that it can accept the case where the parameter is null. Examples are as follows:

//下面创建了一个不包含任何值的Optional实例
//例如,值为'null'
Optional empty = Optional.ofNullable(null);

4.1.3.isPresent

very easy to understand

Returns true if the value exists, false otherwise.

Code similar to the following:

//isPresent方法用来检查Optional实例中是否包含值
if (name.isPresent()) {
  //在Optional实例内调用get()返回已存在的值
  System.out.println(name.get());//输出Sanaulla
}

4.1.4.get

Returns the Optional if it has a value, otherwise throws NoSuchElementException.

In the above example, the get method is used to get the value in the Optional instance. Let's look at an example that throws NoSuchElementException:

//执行下面的代码会输出: No value present 
try {
  //在空的Optional实例上调用get(),抛出NoSuchElementException
  System.out.println(empty.get());
} catch (NoSuchElementException ex) {
  System.out.println(ex.getMessage());
}

4.1.5.ifPresent

If the Optional instance has a value, call the consumer for it, otherwise do not process it

To understand the ifPresent method, you first need to understand the Consumer class. In short, the Consumer class contains one abstract method. This abstract method processes the passed in value, but does not return a value. Java8 supports passing parameters directly through lambda expressions without interfaces.

If the Optional instance has a value, the call to ifPresent() can accept an interface segment or a lambda expression. Code similar to the following:

//ifPresent方法接受lambda表达式作为参数。
//lambda表达式对Optional的值调用consumer进行处理。
name.ifPresent((value) -> {
  System.out.println("The length of the value is: " + value.length());
});

4.1.6.orElse

Returns a value if available, otherwise returns the specified other value.

If the Optional instance has a value, it is returned, otherwise it returns the parameter passed in by the orElse method. Examples are as follows:

//如果值不为null,orElse方法返回Optional实例的值。
//如果为null,返回传入的消息。
//输出: There is no value present!
System.out.println(empty.orElse("There is no value present!"));
//输出: Sanaulla
System.out.println(name.orElse("There is some value!"));

4.1.7.orElseGet

orElseGet is similar to the orElse method, the difference is the default value obtained. The orElse method takes the incoming string as the default value, and the orElseGet method can accept the implementation of the Supplier interface to generate the default value. Examples are as follows:

//orElseGet与orElse方法类似,区别在于orElse传入的是默认值,
//orElseGet可以接受一个lambda表达式生成默认值。
//输出: Default Value
System.out.println(empty.orElseGet(() -> "Default Value"));
//输出: Sanaulla
System.out.println(name.orElseGet(() -> "Default Value"));

4.1.8.orElseThrow

If there is a value, it will be returned, otherwise an exception created by the supplier interface will be thrown.

In the orElseGet method, we pass in a Supplier interface. However, in orElseThrow we can pass in a lambda expression or method to throw an exception if the value does not exist. Examples are as follows:

try {
  //orElseThrow与orElse方法类似。与返回默认值不同,
  //orElseThrow会抛出lambda表达式或方法生成的异常 

  empty.orElseThrow(ValueAbsentException::new);
} catch (Throwable ex) {
  //输出: No value present in the Optional instance
  System.out.println(ex.getMessage());
}

ValueAbsentException is defined as follows:

class ValueAbsentException extends Throwable {

  public ValueAbsentException() {
    super();
  }

  public ValueAbsentException(String msg) {
    super(msg);
  }

  @Override
  public String getMessage() {
    return "No value present in the Optional instance";
  }
}

4.1.9.map

The map method documentation states the following:

If there is a value, call the mapping function on it to get the return value. If the return value is not null, create an Optional containing the return value of mapping as the return value of the map method, otherwise return an empty Optional.

The map method is used to perform a series of operations on the value of the Optional instance. The operation is passed in through a set of lambda expressions that implement the Function interface. If you are not familiar with the Function interface, you can refer to my blog. An example of the map method is as follows:

//map方法执行传入的lambda表达式参数对Optional实例的值进行修改。
//为lambda表达式的返回值创建新的Optional实例作为map方法的返回值。
Optional<String> upperName = name.map((value) -> value.toUpperCase());
System.out.println(upperName.orElse("No value found"));

4.1.10.flatMap

If there is a value, execute the mapping function for it to return an Optional type return value, otherwise return an empty Optional. flatMap is similar to the map(Function) method, the difference is that the mapper return value in flatMap must be Optional. At the end of the call, flatMap will not encapsulate the result with Optional.

The flatMap method is similar to the map method, the difference is that the return value of the mapping function is different. The return value of the mapping function of the map method can be any type T, while the mapping function of the flatMap method must be Optional.

Referring to the map function, an example rewritten using flatMap is as follows:

//flatMap与map(Function)非常类似,区别在于传入方法的lambda表达式的返回类型。
//map方法中的lambda表达式返回值可以是任意类型,在map函数返回之前会包装为Optional。 
//但flatMap方法中的lambda表达式返回值必须是Optionl实例。 
upperName = name.flatMap((value) -> Optional.of(value.toUpperCase()));
System.out.println(upperName.orElse("No value found"));//输出SANAULLA

4.1.11.filter

The filter method filters the value of the Optional instance by passing in the qualification conditions. The document description is as follows:

Returns an Optional containing the value if there is a value and the assertion condition is satisfied, otherwise an empty Optional is returned.

After reading this, you probably already know how to pass a piece of code to the filter method. Yes, a lambda expression can be passed in here. For the filter function we should pass in a lambda expression that implements the Predicate interface. If you are not familiar with the Predicate interface, you can refer to this article.

Now let's take a look at the various uses of filter. The following example introduces two situations that meet the qualification conditions and do not meet them:

//filter方法检查给定的Option值是否满足某些条件。
//如果满足则返回同一个Option实例,否则返回空Optional。
Optional<String> longName = name.filter((value) -> value.length() > 6);
System.out.println(longName.orElse("The name is less than 6 characters"));//输出Sanaulla

//另一个例子是Optional值不满足filter指定的条件。
Optional<String> anotherName = Optional.of("Sana");
Optional<String> shortName = anotherName.filter((value) -> value.length() > 6);
//输出: name长度不足6字符
System.out.println(shortName.orElse("The name is less than 6 characters"));

4.2. Some examples

public class OptionalDemo {

  public static void main(String[] args) {
    //创建Optional实例,也可以通过方法返回值得到。
    Optional<String> name = Optional.of("Sanaulla");

    //创建没有值的Optional实例,例如值为'null'
    Optional empty = Optional.ofNullable(null);

    //isPresent方法用来检查Optional实例是否有值。
    if (name.isPresent()) {
      //调用get()返回Optional值。
      System.out.println(name.get());
    }

    try {
      //在Optional实例上调用get()抛出NoSuchElementException。
      System.out.println(empty.get());
    } catch (NoSuchElementException ex) {
      System.out.println(ex.getMessage());
    }

    //ifPresent方法接受lambda表达式参数。
    //如果Optional值不为空,lambda表达式会处理并在其上执行操作。
    name.ifPresent((value) -> {
      System.out.println("The length of the value is: " + value.length());
    });

    //如果有值orElse方法会返回Optional实例,否则返回传入的错误信息。
    System.out.println(empty.orElse("There is no value present!"));
    System.out.println(name.orElse("There is some value!"));

    //orElseGet与orElse类似,区别在于传入的默认值。
    //orElseGet接受lambda表达式生成默认值。
    System.out.println(empty.orElseGet(() -> "Default Value"));
    System.out.println(name.orElseGet(() -> "Default Value"));

    try {
      //orElseThrow与orElse方法类似,区别在于返回值。
      //orElseThrow抛出由传入的lambda表达式/方法生成异常。
      empty.orElseThrow(ValueAbsentException::new);
    } catch (Throwable ex) {
      System.out.println(ex.getMessage());
    }

    //map方法通过传入的lambda表达式修改Optonal实例默认值。 
    //lambda表达式返回值会包装为Optional实例。
    Optional<String> upperName = name.map((value) -> value.toUpperCase());
    System.out.println(upperName.orElse("No value found"));

    //flatMap与map(Funtion)非常相似,区别在于lambda表达式的返回值。
    //map方法的lambda表达式返回值可以是任何类型,但是返回值会包装成Optional实例。
    //但是flatMap方法的lambda返回值总是Optional类型。
    upperName = name.flatMap((value) -> Optional.of(value.toUpperCase()));
    System.out.println(upperName.orElse("No value found"));

    //filter方法检查Optiona值是否满足给定条件。
    //如果满足返回Optional实例值,否则返回空Optional。
    Optional<String> longName = name.filter((value) -> value.length() > 6);
    System.out.println(longName.orElse("The name is less than 6 characters"));

    //另一个示例,Optional值不满足给定条件。
    Optional<String> anotherName = Optional.of("Sana");
    Optional<String> shortName = anotherName.filter((value) -> value.length() > 6);
    System.out.println(shortName.orElse("The name is less than 6 characters"));

    // 如果为空返回 false;不为空返回 true
    boolean isNull = Optional.ofNullable(对象).isPresent();
  }
}

 5. Reference

​[1] https://wizardforcel.gitbooks.io/java8-new-features/content/1.html

[2] https://blog.csdn.net/ss810540895/article/details/126172285

[3] https://pdai.tech/md/java/java8/java8-optional.html ​

Guess you like

Origin blog.csdn.net/qq_20957669/article/details/131103290