Detailed explanation of Lambda expression ~ simplify anonymous inner class

Posted by jaql on Sun, 20 Feb 2022 05:04:17 +0100

This section will introduce how to use Lambda expressions to simplify the writing of anonymous inner classes. However, Lambda expressions cannot replace all anonymous inner classes and can only be used to replace the abbreviation of Functional Interface. Don't care about the details yet. Let's look at a few examples.

Example 1: abbreviation of nonparametric function

If you need to create a new thread, a common way to write it is as follows:

// JDK7 anonymous inner class writing method
new Thread(new Runnable(){// Interface name
 @Override
 public void run(){// Method name
  System.out.println("Thread run()");
 }
}).start();

The above code passes an anonymous Runnable object to the tree class, and overloads the run() method of the Runnable interface to implement the corresponding logic. This is a common writing method in JDK7 and before. Anonymous inner class eliminates the trouble of naming a class, but it is not simplified enough. In Java 8, it can be simplified as follows:

// JDK8 Lambda expression writing method
new Thread(
  () -> System.out.println("Thread run()")// Omit the interface name and method name
).start();

The above code has the same function as anonymous inner class, but it goes further than anonymous inner class. Here, the connector name and function name are omitted together, which makes it more refreshing to write. If the function body has multiple lines, you can enclose them in curly braces, like this

// JDK8 Lambda expression code block writing method
new Thread(
        () -> {
            System.out.print("Hello");
            System.out.println(" Hoolee");
        }
).start();

Example 2: abbreviation of function with parameters

If you want to sort a string list by string length through a custom comparator, the writing form of Java 7 is as follows:

// JDK7 anonymous inner class writing method
List<String> list = Arrays.asList("I", "love", "you", "too");
Collections.sort(list, new Comparator<String>(){// Interface name
    @Override
    public int compare(String s1, String s2){// Method name
        if(s1 == null)
            return -1;
        if(s2 == null)
            return 1;
        return s1.length()-s2.length();
    }
});

The above code overloads the compare() method of the Comparator interface through the internal class to realize the comparison logic. Lambda expression can be abbreviated as follows:

// JDK8 Lambda expression writing method
List<String> list = Arrays.asList("I", "love", "you", "too");
Collections.sort(list, (s1, s2) ->{// Omit the type of parameter table
    if(s1 == null)
        return -1;
    if(s2 == null)
        return 1;
    return s1.length()-s2.length();
});

The above code is the same as anonymous inner classes. In addition to omitting the interface name and method name, the type of parameter table is also omitted in the code. Thanks to the type inference mechanism of javac, the compiler can infer the type of parameters according to the context information. Of course, when the inference fails, you need to manually specify the parameter type. Note that Java is a strongly typed language, and each variable and object must have an explicit type.

Basis of abbreviation

You may have thought that the basis for using lambda is that there must be a corresponding function interface (function interface refers to the interface with only one abstract method inside). This is consistent with the fact that Java is a strongly typed language, which means that you can't write lambda expressions arbitrarily anywhere in the code. In fact, the type of lambda is the type of the corresponding function interface. Another basis of lambda expression is the type inference mechanism. When the context information is sufficient, the compiler can infer the type of parameter table without explicit naming. Lambda expresses more legal writing forms as follows:

// Written form of Lambda expression
Runnable run = () -> System.out.println("Hello World");// 1
ActionListener listener = event -> System.out.println("button clicked");// 2
Runnable multiLine = () -> {// 3 code block
    System.out.print("Hello");
    System.out.println(" Hoolee");
};
BinaryOperator<Long> add = (Long x, Long y) -> x + y;// 4
BinaryOperator<Long> addImplicit = (x, y) -> x + y;// 5 type inference

In the above code, 1 shows the abbreviation of parameterless function; The abbreviations of parametric functions and the type inference mechanism are shown in 2 places; 3 is the writing method of code block; 4 and 5 show the type inference mechanism again.

Custom function interface

It is easy to customize the function interface. You only need to write an interface with only one abstract method.

// Custom function interface
@FunctionalInterface
public interface ConsumerInterface<T>{
 void accept(T t);
}

The @ FunctionalInterface in the above code is optional, but with this annotation, the compiler will help you check whether the interface complies with the function interface specification. Just like adding the @ Override annotation will check whether the function is overloaded.

With the above interface definition, you can write code similar to the following:

ConsumerInterface<String> consumer = str -> System.out.println(str);

Further, it can also be used as follows:

class MyStream<T>{
 private List<T> list;
    ...
 public void myForEach(ConsumerInterface<T> consumer){// 1
  for(T t : list){
   consumer.accept(t);
  }
 }
}
MyStream<String> stream = new MyStream<String>();
stream.myForEach(str -> System.out.println(str));// Writing Lambda expressions using custom function interfaces

Topics: Java Back-end