Java 8 Lambda Expressions Detailed Manual and Example

Posted by ego0 on Sun, 13 Oct 2019 05:40:59 +0200

If you haven't applied or even know anything about the new features of Java 8, you really need to pay attention to the public number "New View of Programs" and learn about the new features of Java 8 in a series. Lambda expressions have been commonly used in the new framework. If you don't know anything about Lambda, you really need to study this article carefully.

Now let's move on to the main topic of Java 8, Lambda. Let's first look at the pronunciation ([l md]) expression. Note the pronunciation of the word, b is not pronounced, da pronunciation [d].

Why to introduce Lambda expression

Simply put, Lambda was introduced to simplify the code and allow functions to be passed into the method as a parameter of the method. If you have JavaScript programming experience, you will immediately think that this is not closure. Yes, Lambda expressions can also be called closures in Java.

Looking back at Java 8, what would you do if you wanted to pass an implementation class of an interface as a parameter to a method? Either create a class to implement the interface, and then new an object to pass in when calling a method, or use anonymous classes to streamline some code. Take creating a thread and printing a line of logs as an example, using anonymous functions to write as follows:

new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("Welcome to Pay Attention to Public Number: A New Perspective of Procedure");
    }
}).start();

Before Java 8, using anonymous functions was a very concise way to write. Let's look at what the above code would look like with Lambda expressions.

new Thread(() -> System.out.println("Welcome to Pay Attention to Public Number: A New Perspective of Procedure")).start();

Is it concise to explosive?

We all know that Java is an object-oriented programming language, except for some simple data types, everything is object. Therefore, defining functions or methods in Java is inseparable from objects, which means that it is difficult to pass methods or functions directly as parameters, and the emergence of Lambda expressions in Java 8 solves this problem.

Lambda expression enables Java to have the ability of functional programming, but in Java Lambda expression is an object, which must be attached to a special type of object - functional interface, which will be explained in detail later.

A Brief Introduction to Lambda Expressions

Lambda expression is an anonymous function (which is not entirely accurate for Java). Generally speaking, it is a method without declaration, that is, without access modifier, return value declaration and name. The advantage of using Lambda expressions is that they make the code more concise and compact.

The usage scenarios of Lambda expressions are almost identical to those of anonymous classes, both when a function (method) is used only once.

The Grammatical Structure of Lambda Expressions

Lambda expressions are usually written in (param) - > (body) grammar. The basic format is as follows:

//No parameters
() -> body

// 1 Parameters
(param) -> body
// or
(param) ->{ body; }

// Multiple parameters
(param1, param2...) -> { body }
// or
(type1 param1, type2 param2...) -> { body }

The common Lambda expressions are as follows:

// No parameter, the return value is the string "Public Number: New View of Program"
() -> "Public Number: A New Perspective of Procedure";

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

// 1 parameter (number), return 2 times  
x -> 2 * x;

// Two parameters (numbers), returning the difference
(x, y) -> x – y
  
// Two int integers, return and value  
(int x, int y) -> x + y

In contrast to the examples above, we summarize the structure of Lambda expressions:

  • Lambda expression can have 0-n parameters.
  • Parametric types can be explicitly declared, or the compiler can automatically infer types from context. For example (int x) and (x) are equivalent.
  • Multiple parameters are enclosed in parentheses and separated by commas. A parameter can be without parentheses.
  • No parameters are expressed in empty parentheses.
  • The body of a Lambda expression can contain zero, one or more statements, and if there is a return value, it must contain a return value statement. If there is only one parenthesis, it can be omitted. If there is more than one, it must be included in braces (blocks of code).

Functional interface

Functional interface is java 8's call for a special type of interface. This type of interface defines only the interface of the unique abstract method (except the common method of the implied Object object), so it starts with the SAM type of interface (Single Abstract Method).

For example, java.lang.Runnable in the example above is a functional interface, in which only one abstract method of void run() is defined, and @Functional Interface is annotated.

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

@ Functional Interface annotations are used to indicate that the interface should conform to the specification of functional interfaces. There can be only one abstract method besides the common method of the implied Object object. Of course, if an interface only defines an abstract method, Lambda expressions can be used without the annotation, but without the constraints of the annotation, other abstract methods may be added later, resulting in errors where Lambda expressions have been used. Use @Functional Interface to resolve possible errors at the compilation level.

For example, when you annotate @Functional Interface and write two abstract methods in the interface, the following prompts will appear:

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

Through the functional interface, we can also draw a simple conclusion: the interface that can use Lambda expression can only have one abstract method (except the public method of the implied Object object Object).

Note that the method here is limited to abstract methods, but not if there are other static methods in the interface.

Method reference, double colon operation

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

Like the expression of ClassName::methodName or objectName::methodName, we call it Method Reference, which is usually used in Lambda expression.

Take a look at an example:

// Parametric case
NoParamInterface paramInterface2 = ()-> new HashMap<>();
// Replaceable
NoParamInterface paramInterface1 = HashMap::new;

// A parametric case
OneParamInterface oneParamInterface1 = (String string) -> System.out.print(string);
// Replaceable
OneParamInterface oneParamInterface2 = (System.out::println);

// Two-parameter case
Comparator c = (Computer c1, Computer c2) -> c1.getAge().compareTo(c2.getAge());
// Replaceable
Comparator c = (c1, c2) -> c1.getAge().compareTo(c2.getAge());
// It can be further replaced by
Comparator c = Comparator.comparing(Computer::getAge);

For example, we use the functional interface java.util.function.Function to implement a String to Integer function, which can be written as follows:

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

According to the definition of Function interface, Function < T, R >, where T denotes the incoming type and R denotes the return type. Specifically, the application method of Function is implemented, in which the Integer.parseInt method is called.

Through the above instructions, the basic grammar has been completed, and the following examples show how to use it in different scenarios one by one.

Runnable thread initialization example

Runnable thread initialization is a typical application scenario.

// Anonymous Letter Class Writing
new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("Welcome to Pay Attention to Public Number: A New Perspective of Procedure");
    }
}).start();

// lambda expression
new Thread(() -> System.out.println("Welcome to Pay Attention to Public Number: A New Perspective of Procedure")).start();

// lambda expressions need braces if there are multiple lines of code in the method body
new Thread(() -> {
    System.out.println("Welcome to the Public Number");
    System.out.println("New horizon of procedure");
}).start();

Usually, the names of variables inside lambda expressions are shorter, which makes the code shorter.

Example of event handling

Event listening is often used in Swing API programming.

// Anonymous Letter Class Writing
JButton follow =  new JButton("follow");
follow.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("Public Number Concerned: A New Perspective of Procedure");
    }
});

// lambda expression
follow.addActionListener((e) -> System.out.println("Public Number Concerned: A New Perspective of Procedure"));

// lambda expression
follow.addActionListener((e) -> {
    System.out.println("Attention to Public Number");
    System.out.println("New horizon of procedure");
});

List traversal output example

Traditionally, traversing a List basically uses a for loop. After Java 8, List has a forEach method, which can be used to write more concise methods with lambda expressions.

List<String> list = Arrays.asList("Welcome","follow","New horizon of procedure");

// Traditional traversal
for(String str : list){
    System.out.println(str);
}

// lambda expression
list.forEach(str -> System.out.println(str));
// lambda expression writing
list.forEach(System.out::println);

Examples of functional interfaces

The use of the functional interface java.util.function.Function has been seen in the examples above, and there are other classes under the java.util.function package to support Java's functional programming. For example, through Predicate functional interfaces and lambda expressions, you can add logic to API methods and support more dynamic behavior with less code.

@Test
public void testPredicate() {

    List<String> list = Arrays.asList("Welcome", "follow", "New horizon of procedure");

    filter(list, (str) -> ("New horizon of procedure".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("Qualified content:" + content);
        }
    }
}

The method of filter can also be simplified further.

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

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

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

If you don't need "qualified content:" string splicing, you can further simplify:

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

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

list.stream().filter((str) -> ("New horizon of procedure".equals(str))).forEach(System.out::println);

If you need to satisfy two conditions or one of them at the same time, Predicate can merge these conditions into one.

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

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

Stream related examples

In " JAVA8 STREAM New Features Detailed and Practical > The use of Stream has been explained in this article. Do you find that the use of Stream is inseparable from Lambda expressions? Yes, all Stream operations must take Lambda expressions as parameters.

Take Stream's 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);

For more examples, see Stream's< JAVA8 STREAM New Features Detailed and Practical Article 1.

The Difference between Lambda Expressions and Anonymous Classes

  • The difference between keywords: For an anonymous class, the keyword this points to an anonymous class, while for a Lambda expression, the keyword this points to an external class of the class surrounding the Lambda expression, which means the same thing as using this outside the expression.
  • Compilation method: when the Java compiler compiles the Lambda expression, it will convert it to the private method of the class, and then dynamically bind it, and call it through the invokedynamic instruction. The anonymous inner class is still a class, and the compiler automatically names the class and generates the class file at compile time.

The first is a source code of the Servlet Web Server Application Context class in Spring Boot.

private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
   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);
   }
}

Here, this points to the class where the getSelfInitializer method resides.

Summary

So far, the basic use of Java 8 Lambda expressions has been explained. The most important thing is to practice hard to achieve the perfect use of practice. Of course, there may be an adaptation period in the beginning, during which the article can be collected as a manual for reference.

Links to the original text:< Java 8 Lambda Expressions Detailed Manual and Example>


Procedure New Horizon: Wonderful and Growth Can't Be Missed

Topics: Java Lambda Programming Javascript