In-depth understanding of Java Lambda expressions, anonymous functions, closures

foreword

For Lambda expressions, I have always known what I don’t know why. In order to find out what Lambda expressions are, as well as the usage and function of Lambda expressions, this article came into being as a study note and shared it. Welcome to correct and exchange.

What is Lambda

Let's be more serious, Google Translate enters Lambda for translation:

Well, nothing? No way, Baidu Encyclopedia search:

As shown in the figure, for programming, we should focus on Lambda expressions.

What are Lambda expressions

Search for Lamdba expressions to see:

There are pictures and texts, as well as videos, which is very comfortable now. The explanation of excerpted Lambda expression is as follows:

Lambda expression (lambda expression) is an anonymous function . Lambda expression is named based on the lambda calculus in mathematics , which directly corresponds to the lambda abstraction (lambda abstraction). It is an anonymous function, that is, a function without a function name. Lambda expressions can represent closures (note the difference from the traditional sense of mathematics).

Source: Lambda Expressions

From the above description, there are several keywords other than Lambda expression: anonymous function, lambda calculus , and closure. What does all this mean? Let's keep exploring.

Note: λ is the eleventh letter in the Greek alphabet, the corresponding capital is Λ, and the English name is Lambda

What is lambda calculus

It seems that the lambda calculus is more critical, because it is the origin, and understanding this helps us understand Lambda expressions in essence.

λ-calculus (English: lambda calculus, λ-calculus) is a set of formal systems developed from mathematical logic, using variable binding and substitution rules to study how functions are abstractly defined, how functions are applied, and recursion . It was first published by mathematician Alonzo Church in the 1930s. As a widely used computing model, lambda calculus can clearly define what is a computable function, and any computable function can be expressed and evaluated in this form, and it can simulate the computing process of a single tape Turing machine ; although Thus, the lambda calculus emphasizes the application of transformation rules rather than the specific machines that implement them.

Source: Lambda Calculus_Wikipedia

As mentioned above, Lambda calculus is a formal system. What is a formal system? In logic and mathematics, a formal system is composed of two parts, a formal language plus a set of inference rules or transformation rules.

In mathematics , logic , and computer science , a formal language is a language defined by precise mathematical or machine -processable formulas.

Like language in linguistics , formal languages ​​generally have two aspects: syntax and semantics . The branch of mathematics and computer science that studies the syntax of languages ​​is called formal language theory , which studies the syntax of a language without working on its semantics. In formal language theory, a formal language is a collection of certain finite-length strings on an alphabet . A formal language can contain an infinite number of strings.

For a calculus, two things need to be defined: a grammar , which describes how to write legal expressions in the calculus (corresponding to a formal language in a formal system); and a set of rules that let you manipulate expressions symbolically (corresponding to A set of inference rules or transformation rules in a formal system).

The above comes from: Chinese translation of Good Math/Bad Math's Lambda calculus series

composition

Lambda calculus consists of 3 elements: variable ( name ) , function (function) and application (application) :

name

syntax

example

explain

variable ( name )

<name>

x

A variable named "x"

function

λ<parameters>.<body>

λx.x

function with argument 'x' and body 'x'

application

<function><variable or function>

(λx.x)a

Call function "λx.x" with argument "a"

The Greek letter λ (pronounced: Lambda), and a dot (.). λ and dot are used to describe (define) anonymous functions. Functions start with lambda and variables, followed by a dot, and then the body of the function. λ doesn't have any special meaning, it just says that the function starts from here. After λ, the letter before the dot, we call the variable, the part before the dot, is called the head, and the expression after the dot, is called the body.

Question : Why λ?

Answer : chance. Maybe at first Church drew a top symbol up there, like this: (ŷ xy) ab. In the manuscript, he wrote (⋀y.xy) ab. Finally, the typesetting worker changed it into this (λy.xy) ab.

Source: Lambda calculus written by cognitive scientists to Xiaobai

The most basic function is the identity function: λx.x which is equivalent to f(x) = x. The first "x" is the function's argument, and the second is the function body.

Free and Constrained Variables

  • In the function λx.x, "x" is called a bound variable because it is both in the function body and a parameter.
  • In λx.y, "y" is called a free variable because it was never declared beforehand.

Because functions can be part of other functions, a variable can be both bound and free at the same time.

expression

The core concept of Lambda calculus is "expression" ("expression") . An expression may be just a variable (name) or a function (function) or an application (application).

A lambda calculus expression is defined as follows:

<expression> := <name>|<function>|<application> 
<function> := λ <name> . <expression> <application> 
:= <expression><expression> 

Translation form 
<expression> := <identifier character>|<function>|<application>        
<function> := λ<identifier> . <expression> 
<application> := <expression><expression>

Currying

There is a trick in Lambda calculus: if you look at the definition above, you will see that a function (Lambda expression) takes only one parameter. This seems like a big limitation - how can you implement addition with only one argument?

There's nothing wrong with that, because functions are values. You can write a function that takes one parameter, and that function returns a function that takes one parameter, so you can write a function that takes two parameters—essentially the same thing. This is called Currying, named after the great logician Haskell Curry.

For example, we want to write a function to achieve x + y. We are more used to writing something like: lambda xy . plus xy. The way to write a single parameter function is: we write a function with only one parameter, and let it return another function with only one parameter. So x + y becomes a function of one argument x that returns another function that adds x to its own argument:

lambda x. ( lambda y. plus x y )

Now that we know that adding multiple-argument functions doesn't really add anything but simplifies the syntax, so I'll use multi-argument functions when it's convenient as I move on.

algorithm

Lambda calculus has only two real laws: called Alpha and Beta . Alpha is also known as "transformation" and Beta is also known as "status".

Alpha conversion

Alpha is a renaming operation; basically it says that the name of the variable is unimportant: given an arbitrary expression in the Lambda calculus, we can modify the name of a function parameter as long as we also modify all free references to it in the body of the function .

So - for example, if there is an expression like this:

lambda x . if (= x 0) then 1 else x ^ 2 

We can transform x into y using alpha transformation (written alpha[x / y]), so we have:

lambda y . if (= y 0) then 1 else y ^ 2 

This does not change the meaning of the expression in the slightest. But, as we'll see later, this is important because it allows us to implement things like recursion .

Beta protocol

The beauty of the Beta specification is: this rule enables the Lambda calculus to perform any computation that can be done by a machine.

Beta basically means that if you have a function application, you can replace the part of the function body that is related to the corresponding function identifier by replacing the identifier with the parameter value. It sounds convoluted, but it's easy to use.

Suppose we have a function application expression: “ (lambda x . x + 1) 3 “. The so-called Beta specification is that we can implement the function application by replacing the function body (that is, "x + 1"), and replace the referenced parameter "x" with the value "3". So the result of the Beta protocol is " 3 + 1”.

A slightly more complicated example: (lambda y . (lambda x . x + y)) q。this is an interesting expression, because the result of applying this Lambda expression is another Lambda expression: that is, it is a function that creates a function. At this time, the Beta protocol needs to replace all reference parameters "y" with the identifier "q". So, the result is “ lambda x . x + q “.

Source: My Favorite Lambda Calculus - Opening · cgnail's weblog

Closure or complete binding

A term is closed if it has no free variables; otherwise it is open.

A lambda calculus expression is closed if it contains no free variables, otherwise it is open.

Source: http://www.cs.yale.edu/homes/hudak/CS201S08/lambda.pdf

An identifier is said to be bound (constrained) if it is a parameter of a closed Lambda expression; an identifier is free if it is not bound in any enclosing context.

λx.xy: In this expression, y is free, because it is not a parameter of any closed Lambda expression; and x is bound (constrained, because it is a parameter of the closed expression xy of the function definition.

λxy.xy: Both x and y are bound (constrained) in this expression because they are parameters in the function definition.

λy . (λx. xyz): In the inner calculus λx. xyz , y and z are free, and x is bound (constrained). In the full expression, x and y are bound (constrained): x is bound (constrained) by the inner calculus, and y is bound (constrained) by the remaining calculus. But z is still free.

When evaluating a lambda calculus expression, you cannot reference any unbound (constrained) identifiers (variables), because there is no way to know the value of free variables unless there is some other way to provide the value of these free variables. However, when we ignore the context and focus on the subexpressions of a complex expression, free variables are allowed - and it is very important to figure out which variables in the subexpression are free.

evaluate

Evaluation is done by a single transformation rule (variable substitution, often called β-Reduction , β-transformation), which is essentially a lexically scoped substitution.

When evaluating the expression (λx.x)a, we replace all occurrences of "x" in the body of the function with "a".

  • (λx.x)a evaluates to: a
  • (λx.y)a evaluates to: y

You can even create higher order functions:

  • (λx.(λy.x))a evaluates to: λy.a

While the lambda calculus has traditionally only supported single-argument functions, we can create multi-argument functions using a technique called currying .

  • (λx.λy.λz.xyz) is equivalent to f(x, y, z) = ((xy) z)

Sometimes λxy.<body> is used interchangeably with: λx.λy.<body>

Source: Learn X in Y Minutes: Scenic Programming Language Tours

What is an anonymous function

What is an anonymous function? This is very certain, an anonymous function is a function without a name. As for what is a function, the author will not be long-winded. Note that here the author is discussing anonymous functions in the Java programming language.

What do anonymous functions remind you of? Anonymous class , right? An anonymous class is a class without a name . An anonymous function is a function that has no name and does not belong to any class.

Java has always been an object-oriented programming language. This means that everything in Java programming revolves around objects (except certain primitive types for simplicity). Before Java 8, function was part of Class, we need to use class/object to call function. In Java 8 and later versions, there is a function that does not belong to any class. You will definitely be curious. Doesn’t this violate the idea of ​​OOP? If we look into other programming languages ​​like C++, C#, JavaScript; we will find that they are called functional programming languages ​​because we can write functions and use them whenever we need. Some of these languages ​​support object-oriented programming as well as functional programming . What is functional programming? It is recommended to read the article "A Preliminary Study of Functional Programming" by Ruan Yifeng to learn about it.

To understand anonymous functions in depth, it is recommended to first understand anonymous classes. An anonymous class is also essentially an expression. The syntax of an anonymous class expression is similar to a constructor call, except that the class definition is included in the code block.

Anonymous class expressions include the following:

  • new operator;
  • the name of the interface to implement or the class to extend;
  • Parentheses enclosing constructor parameters, just like ordinary class instance creation expressions. Note: If there is no constructor when implementing an interface, a pair of empty brackets is required.
  • A body which is a class declaration body. More specifically, in a body, method declarations are allowed, but not statements.

Because an anonymous class definition is an expression, it must be part of the statement. This anonymous class explains why there is a semicolon after the closing brace.

Anonymous classes Anonymous classes can capture variables just like local classes; they have the same access to local variables of the enclosing scope:

  • An anonymous class can access members of its enclosing class.
  • An anonymous class cannot access local variables in its enclosing scope that are not declared final or effectively final ( Effectively final ).
  • As with nested classes, declarations of types (such as variables) in anonymous classes hide any other declarations of the same name in the enclosing scope.

Local variables accessed by local inner classes and anonymous inner classes in Java must be modified by final to ensure data consistency between inner classes and outer classes. But starting from Java 8, we can add the final modifier without adding the final modifier, which is added by default by the system. Of course, this is not allowed in versions before Java 8. Java refers to this feature as an Effectively final feature.

Anonymous classes also have the same restrictions on their members as local classes:

  • You cannot declare static initialization blocks or member interfaces in an anonymous class.
  • Anonymous classes can have static members, provided they are constant variables.

The following can be declared in an anonymous class:

  • field
  • Extra methods (even if they don't implement any methods of the supertype)
  • Instance initializers
  • local class (local class)

However, constructors cannot be declared in anonymous classes.

Why spend so much time introducing anonymous classes, for example:

public class HelloTest {

    private String text = " world";

    public interface IHello {

        void sayHello();
        
    }

    private void hello() {

        IHello hello = new IHello() {

            @Override
            public void sayHello() {
                System.out.print("Hello " + text);
            }
            
        };

    }

    public static void main(String[] args) {

    }
}

The above code defines an IHello interface, which contains only one abstract method void sayHello(). This is written using an anonymous class, and the compiler will prompt you to replace it with a Lambda expression.

After replacement, it becomes:

public class HelloTest {

    private String text = " world";

    public interface IHello {

        void sayHello();

    }

    private void hello() {

        IHello hello = () -> System.out.print("Hello " + text);

    }

    public static void main(String[] args) {

    }
}

But if the IHello interface contains two or more abstract methods, the compiler will not prompt you to replace them with Lambda expressions. Why?

public class HelloTest {

    private String text = " world";

    public interface IHello {

        void sayHello();

        void printTime();
    }

    private void hello() {

        IHello hello = new IHello() {

            @Override
            public void sayHello() {
                System.out.print("Hello " + text);
            }

            @Override
            public void printTime() {

            }
        };

    }

    public static void main(String[] args) {

    }
}

Because, Java stipulates that if an interface has one and only one abstract method, then the interface is called a functional interface/functional interface (Functional interface) , such as Comparable, Runnable, EventListener, Comparator, etc. In Java8 and later versions, function interfaces can use Lambda expressions instead of anonymous class expressions, which means that function interfaces can be implemented using anonymous functions instead of anonymous classes , which is one of the characteristics of Lambda expressions. Therefore, understanding anonymous classes helps us understand anonymous functions in essence.

But why do we call this kind of interface a functional interface ? Why not call it a Single Method Interface?

This is a good question, if you know anything about functional programming, you know that you can pass code, that is, functions, just like you can pass data or objects to methods. These interfaces have only one abstract method which is used to pass code around like functional programming languages ​​pass functions, that's why they are called functional interfaces.

A simple example:

Runnable runnable = new Runnable(){
    @Override
    public void run(){
        System.out.println("Running without Lambda");
    }
};
new Thread(runnable).start();

// Running with Lambda
new Thread(() -> System.out.println("Running without Lambda")).start();

Looking closely, we are using these interfaces to pass code to the Thread constructor, and for the Thread class, the important thing is the code in the run method. Also, we can easily replace the implementation of the run method, these interfaces are actually strategy interfaces, because this is the implementation of the strategy pattern, where the code that makes up the strategy is injected into the code that runs the strategy at runtime.

An interface with one and only one abstract method is used as a function, so it is called a functional interface , and there is nothing wrong with it.

What if multiple abstract methods are declared in the interface? Can it still be used like this? The answer is no. Because you have to implement all the abstract methods, the Lambda expression will report an error at this time. To prevent multiple abstract methods from being declared in a functional interface, Java 8 provides a @FunctionalInterface annotation for declaring a functional interface, it is not mandatory to use it, but it is a best practice to use it with a functional interface to avoid accidentally adding other method. The sample code is as follows.

/** 
* If an interface has one and only one abstract method, this interface is called a functional interface/functional interface (Functional interface) 
* In Java8 and later versions, functional interfaces can use Lambda expressions instead of anonymous class expressions In other words, functional interfaces can be implemented using anonymous functions instead of anonymous inner classes. 
*/ 
@FunctionalInterface 
public interface IHelloInner { 

    void sayHello(); 

}

Use the @FunctionalInterface annotation to decorate before the interface. When trying to add an abstract method, a compilation error will occur:

The target type of this expression must be a functional interface

However, functional interfaces can add default methods and static methods.

/** 
 * If an interface has one and only one abstract method, this interface is called a functional interface/functional interface (Functional interface) 
 * In Java8 and later versions, functional interfaces can use Lambda expressions instead of anonymous class expressions In other words, functional interfaces can be implemented using anonymous functions instead of anonymous inner classes. 
 * Any number of default methods and static methods can be added 
 */ 
@FunctionalInterface 
public interface IHelloInner { 

    /** 
     * Functional interface/functional interface can add static methods 
     */ 
    static void staticFunction() { 

    } 

    static void staticFunction2() { 

    } 

    /* * 
     * Function interface/function interface can default method 
     */ 
    default void defaultFunction() { 

    } 

        default void defaultFunction2() { 

        } 

        /** 
         * abstract method 
         */ 
        void sayHello();

    }

Think about it, why can't non-functional interfaces support Lambda expressions?

Note: In Java, there are two types of inner classes, local classes (local classes) and anonymous classes. Therefore, an anonymous class must be an inner class, so the term anonymous inner class refers to a class that is an anonymous inner class . Therefore, anonymous classes and anonymous inner classes mean the same thing, don't worry about it. The local class is an inner class defined in the method body. If the local class has no name, it is also an anonymous class. But in turn, an anonymous class is not necessarily a local class, because it is not necessarily in the method body.

There are two special kinds of inner classes: local classes and anonymous classes.

Source: https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html

There are two additional types of inner classes. You can declare an inner class within the body of a method. These classes are known as local classes. You can also declare an inner class within the body of a method without naming the class. These classes are known as anonymous classes.

Source: https://docs.oracle.com/javase/tutorial/java/javaOO/innerclasses.html

Functional interfaces are one of the most important concepts of Java 8, powering Lambda expressions, but many developers put a lot of effort into understanding it without first understanding what functional interfaces do in Java 8, and spend time learning Lambda expressions Formula and Stream API. Unless you know what a functional interface is and how Lambdas relate to it, you won't be able to use the powerful features of Java 8 such as Lambda Expressions and Streams API.

Without knowledge of functional interface , it may not be possible to understand where in the code you can use lambda and it will be difficult to write desired lambda expressions, so it is very important to have a good understanding of functional interface in Java 8. We can see that Java 8 functional interfaces and Lambda expressions help us write smaller and more concise code by removing a lot of boilerplate code.

private void hello() { 
    //Anonymous inner class 
	IHello hello = new IHello() { 
            @Override 
            public void sayHello() { 
                System.out.print("Hello " + text); 
            } 
            
        }; 
    //Anonymous function (Lambda expression formula) 
    IHello hello = () -> System.out.print("Hello " + text); 

}

Note that for access to external class member variables, anonymous functions (Lambda expressions) are no different from ordinary functions, but when accessing local variables within a function, the local variables must be of final type (unchangeable).

Difference between anonymous class and anonymous function

anonymous class

Anonymous functions (Lambda expressions)

inner class with no name

function without name

It can implement an interface containing any abstract method

It can implement an interface containing a single abstract method (functional interface)

It can extend abstract or concrete classes

It cannot extend abstract or concrete classes

Anonymous classes can have instance variables and method local variables

Anonymous functions can only have local variables

Anonymous classes can be instantiated

anonymous function

Inside an anonymous inner class, "this" always refers to the current anonymous class object, not the outer object

Inside an anonymous function, "this" always refers to the current outer class object, the OuterClass object

This is the best option if we are dealing with multiple methods

This is the best option if we are dealing with interfaces

At compile time, a separate .class file will be generated

At compile time, no separate .class files are generated. just cast it as a private method of the outer class

Memory allocation is on-demand whenever we create an object

It resides in the permanent memory of the JVM

Let's look at the compiled bytecode of anonymous classes and anonymous functions:

public class HelloTest {

    private String text = " world";

    public interface IHello {

        void sayHello();

    }

    private void hello() {

        IHello helloAnonymousClass = new IHello() {

            @Override
            public void sayHello() {
                System.out.print("Hello helloAnonymousClass " + text);
            }

        };

        IHello helloAnonymousFunction = () -> System.out.print("Hello helloAnonymousFunction " + text);


    }

    public static void main(String[] args) {

    }
}

Compiled bytecode:

// class version 52.0 (52)
// access flags 0x21
public class com/nxg/app/HelloTest {

  // access flags 0x609
  public static abstract INNERCLASS com/nxg/app/HelloTest$IHello com/nxg/app/HelloTest IHello
  // access flags 0x0
  INNERCLASS com/nxg/app/HelloTest$1 null null
  // access flags 0x19
  public final static INNERCLASS java/lang/invoke/MethodHandles$Lookup java/lang/invoke/MethodHandles Lookup

  // access flags 0x2
  private Ljava/lang/String; text

  // access flags 0x1
  public <init>()V
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
    ALOAD 0
    LDC " world"
    PUTFIELD com/nxg/app/HelloTest.text : Ljava/lang/String;
    RETURN
    MAXSTACK = 2
    MAXLOCALS = 1

  // access flags 0x2
  private hello()V
    NEW com/nxg/app/HelloTest$1
    DUP
    ALOAD 0
    INVOKESPECIAL com/nxg/app/HelloTest$1.<init> (Lcom/nxg/app/HelloTest;)V
    ASTORE 1
    ALOAD 0
    INVOKEDYNAMIC sayHello(Lcom/nxg/app/HelloTest;)Lcom/nxg/app/HelloTest$IHello; [
      // handle kind 0x6 : INVOKESTATIC
      java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
      // arguments:
      ()V, 
      // handle kind 0x7 : INVOKESPECIAL
      com/nxg/app/HelloTest.lambda$hello$0()V, 
      ()V
    ]
    ASTORE 2
    RETURN
    MAXSTACK = 3
    MAXLOCALS = 3

  // access flags 0x9
  public static main([Ljava/lang/String;)V
    RETURN
    MAXSTACK = 0
    MAXLOCALS = 1

  // access flags 0x1002
  private synthetic lambda$hello$0()V
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    NEW java/lang/StringBuilder
    DUP
    INVOKESPECIAL java/lang/StringBuilder.<init> ()V
    LDC "Hello helloAnonymousFunction "
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    ALOAD 0
    GETFIELD com/nxg/app/HelloTest.text : Ljava/lang/String;
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
    INVOKEVIRTUAL java/io/PrintStream.print (Ljava/lang/String;)V
    RETURN
    MAXSTACK = 3
    MAXLOCALS = 1

  // access flags 0x1008
  static synthetic access$000(Lcom/nxg/app/HelloTest;)Ljava/lang/String;
    ALOAD 0
    GETFIELD com/nxg/app/HelloTest.text : Ljava/lang/String;
    ARETURN
    MAXSTACK = 1
    MAXLOCALS = 1
}

In the hello function, use an anonymous class, and finally create an object of the anonymous class.

NEW com/nxg/app/HelloTest$1
DUP
ALOAD 0
INVOKESPECIAL com/nxg/app/HelloTest$1.<init> (Lcom/nxg/app/HelloTest;)V
ASTORE 1

With Lambda expressions, a function called lambda$hello$0() is generated, and an anonymous function is indeed a function in essence.

INVOKEDYNAMIC sayHello(Lcom/nxg/app/HelloTest;)Lcom/nxg/app/HelloTest$IHello; [
    // handle kind 0x6 : INVOKESTATIC
    java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
    // arguments:
    ()V, 
    // handle kind 0x7 : INVOKESPECIAL
    com/nxg/app/HelloTest.lambda$hello$0()V, 
    ()V
]
ASTORE 2

Anonymous classes correspond to classes and objects, and anonymous functions correspond to functions. The essential difference between the two is at least: the difference between classes, objects and functions.

What is a closure

It is really difficult to have an accurate, authoritative and reliable answer.

Closure is translated from the English word closure, which is a word that is not easy to translate. In the computer field, it has three completely different meanings: in the principle of compilation, it is a step in processing grammatical productions; in computational geometry, it is Represents a convex polygon (translated as a convex hull) enclosing a planar set of points; while in the field of programming languages, it represents a function.

The concept of closure first appeared in "The Computer Journal" in 1964. PJ Landin proposed the concepts of applicative expression and closure in the article "The mechanical evaluation of expressions".

In the 1960s, the mainstream programming language was a functional programming language based on lambda calculus, so this initial closure definition used a lot of functional terms. A less precise description is "a lambda expression with a sequence of information". In functional languages, lambda expressions are functions. We can simply understand that a closure is actually just a function bound to an execution environment. This function is not a simple expression printed in a book. The difference between a closure and an ordinary function is that it carries the execution environment , just like people need to bring their own oxygen-absorbing equipment in aliens, this function also has an environment for living in the program. In this classic definition of a closure, a closure consists of two parts. environment part and expression part.

The above content comes from: Cheng Shaofei (winter)'s re-learning front-end column

Note: According to the combination of Cheng Shaofei (winter), combined with other information collected by the author. It can be determined that the concept of closure in programming languages ​​​​is different from that in the field of mathematics. For details, see this article "[Closures] Do you really understand closures and lambda expressions" and the question and answer on stackoverflow What is the difference between a 'closure' and a 'lambda'?

There are many different people who have defined closures, and here are the lists for everyone to see:

  • is a function that references a free variable . This function is usually defined in another external function and refers to a variable in the external function. -- < <wikipedia> >
  • is a callable object that records some information from the scope in which it was created . -- <<Java Programming Ideas>>
  • Is an anonymous code block that can accept parameters and return a return value, and can also refer to and use variables defined in the visible domain around it . -- Groovy ['ɡru:vi]
  • is an expression that has free variables and the context in which those variables are bound .
  • Closures allow you to encapsulate some behavior , pass it around like an object , and it still has access to the original context when it was first declared .
  • An expression (usually a function) that has variables and an environment bound to those variables , so that those variables are also part of the expression.
  • Closures are blocks of code that can contain free (unbound) variables ; these variables are not defined in this code block or any global context, but in the environment in which the code block is defined.

The above content is excerpted from:

JAVA closure - chenjunbiao - 博客园

With so many definitions, it’s really fair to say that the public is right, and the mother-in-law is right, and it’s too abstract to understand! However, there are keywords in each of these definitions: variable, function, context, etc. Closures have important applications in callback functions, functional programming, and Lambda expressions, and closures exist in many popular languages, such as JavaScript, C++, and C#. In order to avoid repeating others’ opinions, adhering to the concept that official is justice, let’s take a look at the milestone document of JDK8 (JDK8 released by Oracle on 2014/03/18 added support for Lambda expressions):

We focus on the new features of Article 126: 126 Lambda expressions and virtual extension methods , click on the link to see:

summarize

Adds lambda expressions (closures) and supporting features to the Java programming language and platform, including method references, enhanced type inference, and virtual extension methods.

There is no special description. According to the official JDK documentation, it seems that Lambda expressions are equivalent to closures .

Looking for other content in the document, I found: Closures for the Java Programming Language (BGGA) , so click in and have a look.

see it? In the Java programming language, the following thing is a closure, so it’s not abstract now (Xiemeixiaogoutou.jpg).

Just kidding, don't take it seriously! Notice that there is a link: BGGA closures specification: Closures for the Java Programming Language (v0.5) , the excerpt is as follows:

Closures for the Java Programming Language (v0.5)

Gilad Bracha, Neal Gafter, James Gosling, Peter von der Ahé

Modern programming languages provide a mixture of primitives for composing programs. Most notably Scheme, Smaltalk, Ruby, and Scala have direct language support for parameterized delayed-execution blocks of code, variously called lambda, anonymous functions, or closures. These provide a natural way to express some kinds of abstractions that are currently quite awkward to express in Java. For programming in the small, anonymous functions allow one to abstract an algorithm over a piece of code; that is, they allow one to more easily extract the common parts of two almost-identical pieces of code. For programming in the large, anonymous functions support APIs that express an algorithm abstracted over some computational aspect of the algorithm. For example, they enable writing methods that act as library-defined control constructs.

Closures for the Java Programming Language (v0.5)

Gerald Bracha, Neil Garft, James Gosling, Peter von der Acher

Modern programming languages ​​provide mixed primitives for writing programs. Most notably, Scheme, Smalltalk, Ruby, and Scala have direct language support for parameterized blocks of deferred execution code known as lambdas , anonymous functions , or closures . These provide a natural way to express Certain abstractions that are currently difficult to express in Java. Programming with small anonymous functions allows one to abstract an algorithm over a piece of code; that is, they allow one to more easily extract the common parts of two nearly identical pieces of code. For large-scale programming, anonymous functions support APIs that express algorithms that abstract some computational aspects of the algorithm. For example, they allow writing methods that act as control structures defined by the library .

Well, maybe you need a conclusion (reassurance), in the Java programming language:

Lambda Expressions are Anonymous Functions and Closures

It looks like this, is a block of code (blocks of code) :

Note that this conclusion is not made by the author, it is compiled from the official Java documentation. If you feel that this conclusion is not consistent with what you have in mind, that is normal. Because when the concept of closure is applied from the data field to the programming field, the concept of closure has changed due to human-to-human transmission. As an Android programmer, follow the official Java documentation and get ready. Of course, if you have better information, it is really desirable, welcome to communicate.

Do you still not understand? It is indeed difficult to understand, because even the Java official did not give an accurate definition (if there is, please correct me). After all, Java did not support Lambda expressions at the beginning, and it was added later by borrowing functional programming ideas from other languages. Up.

Have you noticed? The author did not use the equal sign (=) to indicate the relationship between Lambda expressions, anonymous functions ( Anonymous Functions ), and closures (Closures) , but used a "yes" , why? Because they have different functions (angles of thinking).

Let me explain with a possibly inappropriate example:

You are a programmer, your wife's lover, and your child's father.

Here, it is incorrect to use the expression programmer = lover = father, because programmers, lovers, and fathers are not the same. Although they are one of your identities, they are expressed from different angles ( have different functions).

It's like having a very interesting explanation: the difference between an abstract class and an interface, what an abstract class represents, and what an interface represents can do.

Similarly , Lambda expressions, anonymous functions (Anonymous Functions ), and closures (Closures) are also products (names) that are settled from different perspectives and precipitated for different functions.

The concept of closure has not been clearly defined, and everyone can have their own understanding in their hearts. If you only look at the semantics of the closure itself, it seems that it has the meaning of closing and enclosing, and the corresponding English word closures also means closing. Then we have to think about what is closed (surrounded)? Why close (surround)?

First of all, a closure is an anonymous function, and a function is an independent block of code defined in a class to achieve a certain function. But we don't treat ordinary functions as closures, which shows that there are other restrictions. We try to enumerate these conditions:

  • Condition 1: Must conform to Lambda expression syntax
  • Condition 2: Must be an anonymous function
  • Condition 3: Must be an implementation of a functional interface
  • Condition 3: It must be possible to refer to the member variables of the outer class and the local variables in the function body where it is located
  • Condition 4: It must be possible to infer the type of the parameter and the type of the return value according to the context (environment)

In Java, these conditions are indispensable. In summary, the author understands that: in Java, Lambda expressions are used to represent closures , which exist in the code in the form of anonymous functions .

What exactly is closed (enclosed)?

The closure closes (encloses) the external variables referenced in the Lambda expression (that is, variables not declared by the Lambda expression itself, or called free variables).

Why close (surround)?

Because only by closing (surrounding) these external variables, the Lambda expression can avoid the external variables from being read incorrectly due to the failure of the external environment scope. Lambda expressions will not execute correctly if we don't know where the outer variable comes from and what it is. The purpose of closure (surrounding) is to make the Lambda expression and the external variables it refers to (or free variables, which come from the context, that is, the execution environment where the Lambda is located), to form a complete closed whole.

So what are closures? Closures are anonymous functions bound context (execution environment) using Lambda expression syntax? The author is not sure, just have a look, the most important thing is to have your own thinking.

Of course, in order to avoid copying what others say, the author decided to deepen the understanding of Lambda expressions, anonymous functions, and closures through the use of Lambda expressions. Let’s explore together!

Lambda expression

Because the author is studying Lambda expressions in Java 8, let's take a look at the official documents first.

Java8 has added a lot of new features. The features that may be involved in this article are mainly as follows:

  • Lambda expressions − They enable you to treat functionality as method parameters, or code as data. Lambda expressions allow you to express instances of single-method interfaces (called functional interfaces) more compactly.
  • Method Reference − Method reference provides a very useful syntax to directly refer to a method or constructor of an existing Java class or object (instance). Combined with lambda, method reference can make the language structure more compact and concise, reducing redundant code.
  • Default method − A default method is a method that has an implementation in an interface.
  • New tools − New compilation tools, such as: Nashorn engine jjs, class dependency analyzer jdeps.
  • Stream API − The newly added Stream API (java.util.stream) brings a true functional programming style to Java.
  • Date Time API − Enhanced handling of dates and times.
  • Optional class − Optional class has been part of the Java 8 class library to handle Null Pointer Exceptions.
  • Nashorn, JavaScript Engine − Java 8 provides a new Nashorn javascript engine which allows us to run specific javascript applications on JVM.

For more new features, please refer to the official website: What's New in JDK 8

Lambda expressions. what reminds you Regular expressions, right. Regular expressions are usually used to retrieve and replace text that matches a certain pattern (rule). What is the role of Lambda expression ? There are already answers before, which are used to represent closures . You see, a Lambda expression is essentially an expression.

In Java, a lambda expression is an expression that represents an instance of a functional interface ( Functional interface ) .

Like other types in Java, Lambda expressions are also typed, and their type is a functional interface type. To infer the type, the compiler looks at the left side of the assignment in the lambda expression .

It is important to note that a Lambda expression itself contains no information about the functional interface it is implementing. This information is inferred from the context in which the expression is used.

An expression is a combination of numbers, operators, number grouping symbols (brackets), free variables and constraint variables, etc., obtained by a meaningful arrangement method that can obtain a value. Constrained variables have been assigned a value in the expression, while free variables can be assigned a value outside the expression.

First look at Lambda expressions

As the saying goes: Have you never eaten pork or seen a pig run? If it’s a mule or a horse that comes out for a walk, you’ll know, right? Take a look at the code below:

Without using Lambda expressions:

button.addActionListener(new ActionListener(){
    public void actionPerformed(ActionEvent actionEvent){
        System.out.println("Action detected");
    }
});

Use lambda expressions:

button.addActionListener( actionEvent -> { 
    System.out.println("Action detected");
});

A more obvious example.

Without using Lambda expressions:

Runnable runnable = new Runnable(){
    @Override
    public void run(){
        System.out.println("Running without Lambda");
    }
};

Use lambda expressions:

Runnable runnable = ()->System.out.println("Running from Lambda");

It can be seen that anonymous inner classes are replaced by Lambda expressions. By comparison, it is found that after using Lambda expressions, not only the code becomes simpler, but also the amount of code is reduced a lot, but the readability is improved. The author Take it with a grain of salt (though, at least the code hierarchy is nicer).

In the first example, the Lamba expression is passed into the addActionListener method as a parameter; in the second example, the Lamba expression is used as a function (expression). So, do you think the readability of the code is improved after using Lambda expressions?

In fact, if you use the anonymous class above, the advanced IDE will definitely give you a prompt to replace the Lambda expression. Think about it, why? Let's see how the IDE prompts:

Anonymous new IHello() can be replaced with lambda

Inspection info: Reports all anonymous classes which can be replaced with lambda expressions.

Note that if an anonymous class is converted into a stateless lambda, the same lambda object can be reused by Java runtime during subsequent invocations. On the other hand, when an anonymous class is used, separate objects are created every time. Thus, applying the quick-fix can cause the semantics change in rare cases, e.g. when anonymous class instances are used as HashMap keys.

Lambda syntax is not supported under Java 1.7 or earlier JVMs.

Anonymous new IHello() can be replaced by lambda

Inspection Info: Reports all anonymous classes that can be replaced with lambda expressions.

Note that if you convert an anonymous class to a stateless lambda, the Java runtime can reuse the same lambda object during subsequent invocations. On the other hand, when using anonymous classes, separate objects are created each time. Therefore, in rare cases, applying a quick-fix may result in a change in semantics, such as when anonymous class instances are used as HashMap keys.

Java 1.7 or earlier JVMs do not support Lambda syntax.

Modify the code verification of HelloTest before:

private void hello() {

    IHello helloAnonymousClass = new IHello() {

        @Override
        public void sayHello() {
            System.out.print(this + " Hello helloAnonymousClass " + text + "\n");
        }

    };

    IHello helloAnonymousFunction = () -> {
        System.out.print(this + " Hello helloAnonymousFunction " + text + "\n");
    };

    helloAnonymousClass.sayHello();
    helloAnonymousFunction.sayHello();


}

The result of the operation is as follows:

com.nxg.app.HelloTest$1@34ce8af7 Hello helloAnonymousClass  world
com.nxg.app.HelloTest@b684286 Hello helloAnonymousFunction  world
com.nxg.app.HelloTest$1@880ec60 Hello helloAnonymousClass  world
com.nxg.app.HelloTest@b684286 Hello helloAnonymousFunction  world

It can be found that each time the sayHello function is called, helloAnonymousClass is called by a different object instance of the anonymous class HelloTest$1. The helloAnonymousFunction is called by the external class (closed class). The root cause is as mentioned above in the analysis of the compiled bytecode of HelloTest, so I won’t go into details here.

Lambda expression syntax

Lambda expressions look a lot like function declarations, in fact, lambda expressions are anonymous functions without a name.

A typical Lambda expression syntax looks like this:

(x, y) -> x + y  //This function takes two parameters and return their sum.

As you can see, the parameter types of x and y are not declared, so this Lambda expression can be used in multiple places, and the parameters can match int, Integer or simple String. Depending on context, it will either add two integers or concatenate two strings.

for example:

@FunctionalInterface
interface Operator<T> {
  T process(T a, T b);
}

Operator<Integer> addOperation = (a, b) ->  a + b;
System.out.println(addOperation.process(3, 3));     //Prints 6

Operator<String> appendOperation = (a, b) ->  a + b;
System.out.println(appendOperation.process("3", "3"));  //Prints 33

Operator<Integer> multiplyOperation = (a, b) ->  a * b;
System.out.println(multiplyOperation.process(3, 3));    //Prints 9

Other syntaxes for lambda expressions are:

either
 
(parameters) -> expression           //1
 
or
 
(parameters) -> { statements; }  //2
 
or
 
() -> expression                     //3

Following are the important characteristics of lambda expressions:

  • Optional type declaration: There is no need to declare the parameter type, and the compiler can uniformly identify the parameter value.
  • Optional parameter parentheses: A parameter does not need to define parentheses, but multiple parameters need to define parentheses.
  • Optional curly braces: If the body contains a statement, curly braces are not required.
  • Optional return keyword: If the body has only one expression return value, the compiler will automatically return the value, curly braces need to specify that the expression returns a value.

Lambda Expression Example

Simple example of Lambda expression:

// 1. No parameters are required, the return value is 5   
() -> 5   
  
// 2. Receive a parameter (number type), and return its double value   
x -> 2 * x   
  
// 3. Accept 2 parameters ( number), and return their difference   
(x, y) -> x – y   
  
// 4. Receive 2 int integers, return their sum   
(int x, int y) -> x + y   
  
// 5. Accepts a string object and prints it on the console without returning any value (it looks like returning void)   
(String s) -> System.out.print(s)

Let's see how it works in practice:

public class Java8Tester { 
    
    
   interface MathOperation { 
      int operation(int a, int b); 
   } 
    
   interface GreetingService { 
      void sayMessage(String message); 
   } 
    
   private int operate(int a, int b, MathOperation mathOperation){ 
      return mathOperation.operation(a, b); 
   } 
   
   public static void main(String args[]){ 
      Java8Tester tester = new Java8Tester(); 
        
      // type declaration 
      MathOperation addition = (int a, int b) -> a + b; 
        
      // no type declaration 
      MathOperation subtraction = (a, b) -> a - b; 
        
      // return statement in braces 
      MathOperation multiplication = (int a, int b) -> { return a * b; };
        
      // No braces and return statement 
      MathOperation division = (int a, int b) -> a / b; 
        
      System.out.println("10 + 5 = " + tester.operate(10, 5, addition)); 
      System .out.println("10 - 5 = " + tester.operate(10, 5, subtraction)); 
      System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication)); 
      System .out.println("10 / 5 = " + tester.operate(10, 5, division)); 
        
      // without brackets 
      GreetingService greetService1 = message -> 
      System.out.println("Hello " + message); 
        
      // with Brackets 
      GreetingService greetService2 = (message) -> 
      System.out.println("Hello " + message); 
        
      greetService1.sayMessage("Runoob");
      greetService2.sayMessage("Google");
   }
}

Characteristics of Lambda Expressions

Lambda expression parameters can have zero, one or more.

(x, y) -> x + y
(x, y, z) -> x + y + z

The body of a lambda expression can contain zero, one, or more statements.

If the body of the Lambda expression has only one statement, the braces are not mandatory, and the return type of the Lambda expression is the same as that of the body expression.

When there are multiple statements in the body, those statements must be enclosed in curly braces.

(parameters) -> { statements; }

The type of the parameter can be explicitly declared or inferred from the context. Multiple parameters need to be enclosed in parentheses and separated by commas. Empty parentheses are used to denote empty parameters.

() -> expression

When there is a single parameter, parentheses are not enforced if its type can be inferred.

a -> return a * a;

Lambda expressions cannot have a throws clause, and the parameter type is inferred from the context in which the parameter is used and the body in which the parameter is located.

Lambda expressions cannot be generic, i.e. they cannot declare generic parameters.

Variable scope for lambda expressions

Local variables accessed by local classes (local classes) and anonymous classes in Java must be modified by final to ensure data consistency between inner classes and outer classes. But starting from Java 8, we can add the final modifier without adding it by the system by default. Of course, this is not allowed in versions before Java 8. Java refers to this feature as an Effectively final feature. Since the Lambda expression is an anonymous function, its variable scope is consistent with that of the anonymous class.

Further reading: Java Final and Effectively Final

You may have heard of the concept of free variables , but in Java, the author did not find a relevant introduction, and it is likely to be a unique concept in other programming languages ​​​​or fields. But the latter does not prevent us from understanding free variables in the same way .

In Java, member variables:

  • Member variables in a class - these are called fields.
  • Variables within methods or code blocks - these are called local variables.
  • Variables in method declarations - these are called parameters.

It can be considered that as long as the variable is not declared in the Lambda expression, it is a free variable for the Lambda expression .

A parameter or local variable with the same name as a local variable is not allowed in a Lambda expression

variable scope

The difference between member variables and local variables:

Member variables:

1. Member variables are defined in the class and can be accessed throughout the class.

2. Member variables are established with the establishment of the object, disappear with the disappearance of the object, and exist in the heap memory where the object is located.

3. Member variables have default initialization values.

local variable:

1. Local variables are only defined in the local scope, such as: in functions, statements, etc., and are only valid in the area to which they belong.

2. Local variables exist in the stack memory, and the scope of action ends, and the variable space will be released automatically.

3. There is no default initialization value for local variables

The principles that need to be followed when using variables are: the principle of proximity, first look for it in the local scope, and use it if there is one; then look for it at the member position.

local class (local class)

Anonymous class (anonymous inner class)

Lambda expressions (anonymous functions/closures)

Member variables

Is it accessible (yes)

Whether it can be modified (non-final can)

Is it accessible (yes)

Whether it can be modified (non-final can)

Is it accessible (yes)

Whether it can be modified (non-final can)

local variable

Is it accessible (yes)

Is it possible to modify (no)

Is it accessible (yes)

Is it possible to modify (no)

Is it accessible (yes)

Is it possible to modify (no)

But turn it into an atomic class to implement

For local variables, those before Java 8 must be modified by final to be used by local classes (local classes),

Anonymous class (anonymous inner class), Lambda expression (anonymous function/closure) access, otherwise compilation and compilation will report an error.

Why can local classes and anonymous classes refer to member variables of external classes? Because the inner class holds a reference to the outer class, and even if the variable is declared as private, it can also be referenced. for example:

/** 
 * Outer class (closed class) 
 */ 
public class OuterClass { 
    //private member variable 
    private String privateText = "privateText"; 
    //private static variable 
    private static String privateStaticText = "privateStaticText"; 
    //public static Variable 
    public static String publicStaticText = "publicStaticText"; 

    /** 
     * If an interface has one and only one abstract method, this interface is called a functional interface/functional interface (Functional interface) 
     * In Java8 and later versions, the function Interfaces can use Lambda expressions instead of anonymous class expressions, that is to say, functional interfaces can be implemented using anonymous functions instead of anonymous inner classes. 
     * Any number of default and static methods can be added 
     */ 
    @FunctionalInterface 
    public interface IHelloInner { 

        /** 
         * Abstract method 
         */ 
        void sayHello();

    } 

    /** 
     * Member function 
     * 
     * @param iHelloOuter local variable 
     */ 
    private void hello(IHelloOuter iHelloOuter) { 

        String localText = "localText "; 

        /** 
         * Local class (local class) 
         */ 
        class LocalClass implements IHelloInner { 

            @Override 
            public void sayHello() { 
                //You can use the properties of the external class 
                System.out.print("Hello LocalClass " + privateText); 
                System.out.print("Hello LocalClass " + privateStaticText); 
                System.out.print("Hello LocalClass " + publicStaticText);
                System.out.print("Hello LocalClass " + localText);
            }
        }

    }
}
//编译后的LocalClass持有OuterClass的引用
class OuterClass$1LocalClass implements IHelloInner {
    OuterClass$1LocalClass(OuterClass var1, String var2) {
        this.this$0 = var1;
        this.val$localText = var2;
    }

    public void sayHello() {
        System.out.print("Hello LocalClass " + OuterClass.access$000(this.this$0));
        System.out.print("Hello LocalClass " + OuterClass.access$100());
        System.out.print("Hello LocalClass " + OuterClass.publicStaticText);
        System.out.print("Hello LocalClass " + this.val$localText);
    }
}

Not to mention the Lambda expression, which is the member function of the external class. It was introduced when I explained the closure earlier, so I won’t go into details.

So if you want to modify local variables, can you? The answer is no. If necessary, the Effectively final variable is officially suggested to be changed to an Atomic class for implementation.

variable lifetime

The scope of a variable refers to the range in which the variable exists, and only within this range can the program code access it. When a variable is defined, its scope is determined. The scope of a variable determines the life cycle of the variable, indicating that the life cycle is different if the scope is different.

The life cycle of a variable refers to the process from when a variable is created and memory space is allocated to when the variable is destroyed and the memory space it occupies is cleared.

The above picture comes from: Overview of java method member variables and local variables

It can be seen that the local variables are released after the function returns the result. But if local variables are used by Lambda expressions, this will undoubtedly prolong the lifetime of local variables. This is actually not a problem, just like recursion, function calls function.

Method References for Lambda Expressions

Method References

You use lambda expressions to create anonymous methods. Sometimes, however, a lambda expression does nothing but call an existing method. In those cases, it’s often clearer to refer to the existing method by name. Method references enable you to do this; they are compact, easy-to-read lambda expressions for methods that already have a name.

method reference

You use lambda expressions to create anonymous methods. However, sometimes lambda expressions do nothing but call an existing method. In these cases, it is often clearer to refer to an existing method by name. Method references allow you to do this; they are compact, easy-to-read lambda expressions for methods that already have names.

The description of method references comes from:

https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html

Following is some syntax of method reference in Java 8, there are four method references:

type

syntax

example

referencing a static method

ContainingClass::staticMethodName

Person::compareByAge
MethodReferencesExamples::appendStrings

Refer to an instance method of a specific object

containingObject::instanceMethodName

myComparisonProvider::compareByName
myApp::appendStrings2

Refers to an instance method of any object of a particular type

ContainingType::methodName

String::compareToIgnoreCase
String::concat

reference to the constructor

ClassName::new

HashSet::new

Not much to say about the specifics, the official documents are written in more detail, it is recommended to read and learn. Personally, although method reference refers to a method instead of calling a method, method reference looks more like a syntactic sugar, which simplifies the use of Lambda expressions and makes it easier to use existing functions.

Advantages and functions of Lambda expressions

Lambda expressions bring many of the benefits of functional programming to Java. Like most OOP languages, Java is built around classes and objects, and only treats classes as their first-class citizens. Other important programming entities, such as functions, take a back seat.

But in functional programming, we can define functions, give them reference variables, pass them as method parameters, etc. JavaScript is a good example of functional programming, we can pass callback methods to Ajax calls and so on.

Note that before Java 8, we could do everything with anonymous classes that could be executed using Lambda expressions, but they used a very concise syntax to achieve the same result. Let's see a comparison of the same method implementation using both techniques.

//Use lambda expression 
Operator<Integer> addOperation = (a, b) -> a + b; 

//Use anonymous class 
Operator<Integer> addOperation = new Operator<Integer>() { 
  @Override 
  public Integer process(Integer a , Integer b) { 
    return a + b; 
  } 
};

Object Orientation is not bad, but it brings a lot of verbose details to the program. As the above example shows, the actual used part is the code in the process() method, and all the remaining codes are Java language constructs.

Java 8 Functional Interfaces and Lambda Expressions help us write smaller and more concise code by removing a lot of boilerplate code. that's all? Instead of discussing the advantages of Lambda expressions, it is better to discuss its role. As for the advantages, it must be compared to draw conclusions, but after all, Lambda expressions belong to the idea of ​​functional programming. If you want to compare, it feels exaggerated, right?

What does the Lambda expression do? Or what is its meaning (value), is there any difference in Java programming with and without Lambda expressions ? Is it just to reduce the amount of code? To understand the value of Lambda expressions , you must first understand what functional programming is . The answers to the above questions are in Ruan Yifeng 's "A Preliminary Exploration of Functional Programming" article, which depends entirely on your own comprehension.

There are also related documents here to talk about this issue: Why do we need Lambda Expression , the key content in the author's arrangement is as follows:

  1. Reduced Lines of Code (reduce the number of lines of code)

Although anonymous classes are used everywhere, they still have many problems. The first major problem is complexity. These classes make the code hierarchy look messy and complicated, also known as Vertical Problem. Comparing Lambda expressions with anonymous classes, the amount of code is indeed reduced a lot. Students who like to use syntactic sugar will definitely fall in love with it.

  1. Sequential and Parallel Execution Support ( sequential and parallel execution support )

This requires Lambda expressions combined with FunctionalInterface Lib, forEach, stream(), and method references to be reflected. For details, see Mingqi 's answer on Mouhu: What is the use of Lambda expressions? how to use? Both pictures and texts, vivid examples.

  1. Passing Behaviors into methods

Simply understood, Lambda expressions allow us to treat functionality as method parameters, or code as data. Lambda expressions can express instances of single-method interfaces (called functional interfaces) more compactly.

  1. Higher Efficiency with Laziness

How do you say this? Anyway, Lambda expressions refer to existing functions through method references, so that writing code is more efficient.

Source here: Java 8 Functional Interfaces - JournalDev

Implementation principle of Lambda expression

In Java 8, Lambda expressions are implemented with invokedynamic. Specifically, the Java compiler utilizes the invokedynamic instruction to generate an adapter that implements a functional interface .

I won’t introduce much, you can read it if you are interested

Zheng Yudi (Oracle Senior Researcher, Ph.D. in Computer Science) "In-depth Disassembly of the Java Virtual Machine" , you can read the chapters for free:

08 | How does the JVM implement invokedynamic? (superior)

09 | How does the JVM implement invokedynamic? (Down)

Or you can read this:

Dynamic method call of JVM: invokedynamic

write at the end

Regarding the concept of closures, after reading many introductory articles about closures, the author feels pressure. Because of the understanding of closures, there are really a thousand Hamlets for a thousand readers. This article is purely a note for learning Java Lambda expressions, anonymous functions, and closures. There may be errors and omissions in the expression of the article, and corrections are welcome. The excerpts are also marked with the source. At the same time, thank you very much for your patience in reading the entire article. It is not easy to insist on writing original and practical articles. If this article happens to be helpful to you, you are welcome to like and comment on the article. Your encouragement is the author's unremitting motivation ,Thanks again.

References

Official Java Virtual Machine Specification Documentation

JDK 8 Milestones

OpenJDK: Closures

Closures (Lambda Expressions) for the Java Programming Language

JEP 126: Lambda Expressions & Virtual Extension Methods

Lambda Expressions in Java

Java 8 Functional Interface and Lambda Expression

Java Lambda expressions

What is a Functional interface in Java 8? @Functional Annotation and Examples

Anonymous classes and Lambda expressions in Java

Java 8 Functional Interfaces

Guess you like

Origin blog.csdn.net/xiangang12202/article/details/122916258
Recommended