Why Lambda expressions

Before Java8, what did you do if you wanted to pass the implementation class of an interface as a parameter to a method? You can either create a class that implements the interface and then new out an object that is passed in when a method is called, or you can simplify some of the code by using anonymous classes. For example, to create a thread and print a log line, the anonymous function is written as follows:

new Thread(new Runnable() {
    @Override
    public void run(a) {
        System.out.println("Lambda expressions that even white people can understand.");
    }
}).start();
Copy the code

Before java8, using anonymous functions was pretty neat, but let’s see what this code looks like with Lambda expressions.

new Thread(() -> System.out.println("Lambda expressions that even white people can understand.")).start();
Copy the code

Lambda expression syntax structure

Lambda expressions are usually written using the (param)->(body) syntax, with the following basic format

// No arguments
() -> body
// 1 parameter
(param) -> body
/ / or
(param) ->{ body; }
// Multiple parameters
(param1, param2...) -> { body }
/ / or
(type1 param1, type2 param2...) -> { body }
Copy the code

Common Lambda expressions are as follows:

// No arguments, return the string "Lambda expression"() - >"Lambda expressions that even white people can understand.";

// 1 String argument, print the result directly
(System.out::println);
/ / or
(String s) -> System.out.print(s)

// 1 argument (number), returns 2 times the value
x -> 2 * x;

// 2 arguments (numbers), return the difference(x, y) -> x -- y// 2 int integers, return sum
(int x, int y) -> x + y
Copy the code

Using the above example, let’s summarize the structure of a Lambda expression:

  • Lambda expressions can have 0 to n arguments.
  • Parameter types can be declared explicitly, or they can be inferred automatically by the compiler from the context. For example, (int x) and (x) are equivalent.
  • Multiple parameters are enclosed in parentheses and separated by commas. A parameter can be left out of parentheses.
  • No arguments are represented by empty parentheses.
  • The body of a Lambda expression can contain zero, one, or more statements, and must contain a return value statement if any. Omit the braces if there is only one. If there is more than one, it must be enclosed in braces (code blocks).

Functional interface

Functional Interface is Java8’s name for a special type of Interface. This type of interface defines only a Single Abstract Method interface (except for the implied public methods of Object objects), so it was originally used as a SAM type interface (Single Abstract Method).

For example, the java.lang.runnable example in the above example is a FunctionalInterface that defines only an abstract method of void run() internally and annotates @functionalinterface on that interface.

@FunctionalInterface
public interface Runnable {
    public abstract void run(a);
}
Copy the code

The @functionalinterface annotation is used to indicate that the interface complies with the specification of a FunctionalInterface, and that only one abstract method may be available in addition to the implied public method of an Object Object. Of course, if an interface defines only one abstract method, it is possible to use Lambda expressions without the annotation, but without the constraints of the annotation, other abstract methods may be added later, causing errors where Lambda expressions are already used. @FunctionalInterface addresses possible errors at the compile level.

For example, when annotating @functionalInterface and writing two abstract methods inside the interface, the following message appears:

Multiple non-overriding abstract methods found in interface com.secbro2.lambda.NoParamInterface
Copy the code

A simple conclusion can also be drawn from functional interfaces: interfaces that use Lambda expressions can have only one abstract method (except for the implicit public method of an Object).

Methods here are limited to abstract methods, not if there are other static methods on the interface.


Method reference, double colon operation

The [method reference] format is class name :: method name

Expressions such as ClassName::methodName or objectName::methodName are called Method references and are commonly used in Lambda expressions

// No parameter
NoParamInterface paramInterface2 = ()-> new HashMap<>();
// Can be replaced by
NoParamInterface paramInterface1 = HashMap::new;

// A parameter case
OneParamInterface oneParamInterface1 = (String string) -> System.out.print(string);
// Can be replaced by
OneParamInterface oneParamInterface2 = (System.out::println);

// Two parameter cases
Comparator c = (Computer c1, Computer c2) -> c1.getAge().compareTo(c2.getAge());
// Can be replaced by
Comparator c = (c1, c2) -> c1.getAge().compareTo(c2.getAge());
// Can be replaced by
Comparator c = Comparator.comparing(Computer::getAge);
Copy the code

For example we use functional interface Java. Util. The function. The function to achieve a String to an Integer function, can be written as follows:

Function<String, Integer> function = Integer::parseInt;
Integer num = function.apply("1");
Copy the code

Function is defined according to the Function interface, where T represents the incoming type and R represents the return type. The apply method implements Function and calls integer.parseInt.

Now that the basic syntax is complete, the following is a step-by-step example of how to use it in different scenarios.


Runnable thread initialization example

Runnable thread initialization is a typical application scenario.

// anonymous function
new Thread(new Runnable() {
    @Override
    public void run(a) {
        System.out.println("Lambda expressions that even white people can understand.");
    }
}).start();

// lambda expressions
new Thread(() -> System.out.println("Lambda expressions that even white people can understand.")).start();

Lambda expressions need braces if there are more than one line of code in the method body
new Thread(() -> {
    System.out.println("Lambda expressions that even white people can understand.");
    System.out.println("Lambda expressions that even white people can understand.");
}).start();
Copy the code

It is common to abbreviate the names of variables inside lambda expressions to make code shorter.


Event Handling Example

Event listeners commonly used in Swing API programming.

// anonymous function
JButton follow =  new JButton("Attention");
follow.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("Lambda expressions that even white people can understand."); }});// lambda expressions
follow.addActionListener((e) -> System.out.println("Lambda expressions that even white people can understand."));

// lambda expressions
follow.addActionListener((e) -> {
    System.out.println("Lambda expressions that even white people can understand.");
    System.out.println("Lambda expressions that even white people can understand.");
});
Copy the code

Example list traversal output

Traditionally, a List is traversed by a for loop. After Java8, the List has a forEach method, which can be used with lambda expressions to write more concise methods.

List<String> list = Arrays.asList("White"."Expression"."Lambda");
// Traditional traversal
for(String str : list){
    System.out.println(str);
}
// lambda expressions
list.forEach(str -> System.out.println(str));
// lambda expressions
list.forEach(System.out::println);
Copy the code

Examples of functional interfaces

Have seen in the above example Java. The functional interface util. The function. The use of the function, in Java. Util. Function under the package and the other in class, to support Java functional programming. For example, using the Predicate functional interface and lambda expressions, you can add logic to API methods to support more dynamic behavior with less code.

@Test
public void testPredicate(a) {

    List<String> list = Arrays.asList("White"."Expression"."Lambda");

    filter(list, (str) -> ("Expression".equals(str)));

    filter(list, (str) -> (((String) str).length() == 5));

}

public static void filter(List<String> list, Predicate condition) {
    for (String content : list) {
        if (condition.test(content)) {
            System.out.println("Eligible content:"+ content); }}}Copy the code

The writing method of filter method can be further simplified:

list.stream().filter((content) -> condition.test(content)).forEach((content) ->System.out.println("Eligible content:" + content));

list.stream().filter(condition::test).forEach((content) ->System.out.println("Eligible content:" + content));

list.stream().filter(condition).forEach((content) ->System.out.println("Eligible content:" + content));
Copy the code

If you don’t need concatenation of the “qualified content:” string, you can simplify it further:

list.stream().filter(condition).forEach(System.out::println);
Copy the code

If the criteria for calling the filter method are also included, the contents of the test method can be implemented with one line of code:

list.stream().filter((str) -> ("Lambda expressions that even white people can understand.".equals(str))).forEach(System.out::println);
Copy the code

If you need to satisfy both conditions or one of them, Predicate can combine these conditions into one.

Predicate start = (str) -> (((String) str).startsWith("Procedures"));
Predicate len = (str) -> (((String) str).length() == 5);

list.stream().filter(start.and(len)).forEach(System.out::println);
Copy the code

Stream examples

The use of STREAM was explained in the article Java8 STREAM New Features. Whether or not you find a Stream can’t be used without Lambda expressions. Yes, all Stream operations must take Lambda expressions as arguments.

Take the Stream map method as an example:

Stream.of("a"."b"."c").map(item -> item.toUpperCase()).forEach(System.out::println);
Stream.of("a"."b"."c").map(String::toUpperCase).forEach(System.out::println);
Copy the code

Lambda expressions differ from anonymous classes

  • The keyword difference: For anonymous classes, the keyword this refers to the anonymous class, whereas for Lambda expressions, the keyword this refers to the class enclosing the Lambda expression, which means the same thing as using this outside the expression.
  • How it is compiled: When the Java compiler compiles a Lambda expression, it converts it to a private method of the class, which is then dynamically bound and invoked using the InvokeDynamic directive. An anonymous inner class is still a class that the compiler automatically names and generates a class file at compile time. In the first article to Spring the Boot ServletWebServerApplicationContext class as an example of a source code:
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer(a) {
   return this::selfInitialize;
}

private void selfInitialize(ServletContext servletContext) throws ServletException {
   prepareWebApplicationContext(servletContext);
   registerApplicationScope(servletContext);
   WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(),servletContext);
   for(ServletContextInitializer beans : getServletContextInitializerBeans()) { beans.onStartup(servletContext); }}Copy the code

Where this refers to the class for the getSelfInitializer method.


summary

At this point, the basic use of Java8 Lambda expression has been explained, the most important thing is to practice, to achieve the use of practice makes perfect. Of course, there may be an adjustment period at first, during which you can bookmark this article as a handbook. Java8 Lambda expression details and Examples