Now that we have learned how to use the lamdba expression, students will be curious how Lambda is implemented. Let’s explore the underlying implementation of Lambda expressions

@FunctionalInterface
public interface Swimmable {
    public abstract  void swimming(a);
    
}

Copy the code
public class demo01LambdaImpl {
    public static void main(String[] args) {
        // Anonymous inner classes are compiled to form a new class,.$
        goSwimming(new Swimmable() {
            @Override
            public void swimming(a) {
                System.out.println("I'm swimming in the anonymous inner class"); }}); }Lambda expression with no return value for the argument
    private static void goSwimming(Swimmable swimmable) { swimmable.swimming(); }}Copy the code

Anonymous inner class

We see that anonymous inner classes can be compiled to produce a class :demo01LambdaIntro$1.class

Decompress this class demo01LambdaIntro$1.class, resulting in the following code

package com.example.jdk.demo01Lamdba;

public class demo04LambdaImplThe $1implements Swimmable {
    public demo04LambdaImpl$1() {}public void swimming(a) {
            System.out.println("I am the swim of Lambda."); }}Copy the code

Lambda expressions

Let’s take a look at the effect of Lambda. The code is as follows

package com.example.jdk.demo01Lamdba;

public class demo04LambdaImpl {
    public demo04LambdaImpl(a) {}public static void main(String[] args) {
        goSwimming(() -> {
            System.out.println("I am the swim of Lambda.");
        });
    }

    private static void goSwimming(Swimmable swimmable) { swimmable.swimming(); }}Copy the code

Running the program, the console gets the expected result, but does not produce a new class, which means Lambda does not produce a new class at compile time. In fact, we use a tool that comes with JDK: Javap, decompile bytecode, check bytecode instructions:

On the console enter:

Javap -c -p File name Class -c: disassembler code -p: all classes and membersCopy the code

The decompilation results are as follows:

Lambda generates a new private static method in the class, and the methods in the Lambda expression are placed in the new method:

private static void lambda$main$0(a);Copy the code

Lambda $main$0 is the same as lambda$main$0.

public class demo04LambdaImpl {
 
    public static void main(String[] args) {... }private static void lambda$main$0() {
            System.out.println("I am the swim of Lambda."); }}Copy the code

$main$0 = $0; $main$0 = $0; $main = $0;

How do I call this method? Actually Lambda expressions at run time will generate an inner class, in order to verify whether to generate an inner class, you can add – Djdk at run time. Internal. Lambda. After dumpProxyClasses plus the parameters, will produce the inner class runtime class code output to a file. Use the Java command as follows:

Java - Djdk. Internal. Lambda. DumpProxyClasses run the package name. The name of the classCopy the code

When the execution is complete, a new class is generated, with the following decompilation effect:

public class demo04LambdaImpl {
 
    public static void main(String[] args) {
  			goSwimming(new Swimming() -> {
            demo04LambdaImpl.lambda$main$0(a); }); }private static void lambda$main$0() {
            System.out.println("I am the swim of Lambda."); }}Copy the code

Summary:

Anonymous inner classes form a class file at compile time

A Lambda expression forms a class when the program is run

  1. Add a method to the class whose body is the code for the Lambda expression
  2. It also forms an anonymous inner class that implements interfaces and overrides abstract methods
  3. The newly generated method is called in the override method of the interface

The ellipsis pattern for Lambda expressions

Based on the Lambda standard format, the rules for using ellipsis are as follows:

  • The type of the argument in the parentheses can be omitted
  • If there is only one argument inside the parentheses, the parentheses can be omitted
  • If there is only one statement in the braces, you can omit the braces, return keyword, and statement semicolon

Omit the front:

(int a ) ->{
	return new Person();
}
Copy the code

After the omitted:

a->new Person()
Copy the code

Prerequisites for Lambda expressions

The syntax of Lambda expressions is very concise, but Lambda expressions are not used lightly. There are several conditions that need to be noted when using Lambda expressions:

  1. To use a Lambda, the parameter or local variable type of a method must be an interface
  2. An interface has one and only one abstract method
public interface Flyable{
  public abstract void fly(a);
}
Copy the code
public class demo06LambdaCondition {
    public static void main(String[] args) {
        test(()-> System.out.println("I can fly."));

    }

    public static void test(Flyable flyable){
        flyable.fly();
    }
    Flyable f = ()->{
        System.out.println("I can fly.");
    };

    /** We can use Lambda expressions if only one abstract method interface is called a functional interface@FunctionalInterfaceCheck if this interface has only one abstract method */
    @FunctionalInterface
    interface Flyable{
        public abstract void fly(a); }}Copy the code
  1. The parameter or variable of a method is of type interface
  2. There can only be one abstract method in this interface

Understand the difference in the use of Lambda and anonymous inner classes

  1. The types required are different

    Anonymous inner classes: Required types can be classes, abstract classes, interfaces

    Lambda expressions: The required type must be an interface

  2. The number of abstract methods varies

    Anonymous inner classes: The number of abstract methods in the interface required is arbitrary

    Lambda expressions: The required interface can have only one abstract method

  3. Realize the principle of

    Anonymous inner classes: Form class files after compilation

    Lambda expressions: Dynamically generate pre-class JDK8 interfaces at runtime

interfaceThe interface name{static constant; Abstract method; }Copy the code

Interfaces can also have default methods and static methods

JDK8 interface

interfaceThe interface name{static constant; Abstract method; Default method; Static method; }Copy the code

The interface introduces a background of default methods

Before JDK8, there were only abstract methods in the interface, which had the following problems:

If you add an abstract method to an interface, all implementation classes must override the abstract method. It is not conducive to interface expansion.

public interface A {
    public abstract void test01(a);
    //public abstract void test02();

}
class  B implements  A{

    @Override
    public void test01(a) {
        System.out.println("B test01"); }}class C implements  A{

    @Override
    public void test01(a) {
        System.out.println("C test01"); }}Copy the code

So, in JDK8, we added a default method to the interface, which looks like this:

public interface Map<K.V> {	
	default void forEach(BiConsumer<? super K, ? super V> action) {}}Copy the code

The default method implementation class of the interface does not have to be rewritten, can be used directly, the implementation class can also be rewritten according to the need, so as to facilitate the expansion of the interface

The definition format of the default method of the interface

 interfaceThe interface name{modifierdefaultReturn value type method name () {.... }}Copy the code

The use of default methods on the interface

  1. Implementation classes call interface default methods directly
  2. Implements class override interface default methods
public class Demo2UseDefaultFunction {
    public static void main(String[] args) {
        BB bb = new BB();
        bb.test01();

        CC cc = newCC(); cc.test01(); }}interface AA {
    public default void test01(a) {
        System.out.println("I'm the default method for interface AA."); }}// The implementation class can be used directly
class BB implements AA {}The implementation class can override the default method
class CC implements AA {
    @Override
    public void test01(a) {
        System.out.println("I am test01() overwritten by CC"); }}Copy the code

Static methods in interfaces

 interfaceThe interface name{modifierstaticReturn value type method name () {.... }}Copy the code
public class Demo2UseStaticFunction {
    public static void main(String[] args) {
        BBB bbb = new BBB();
        // bbb.test01(); You can't use
        // Use the interface name. Static method name callsAAA.test01(); }}interface AAA {
    public static void test01(a) {
        System.out.println("I'm a static method on interface AA."); }}// The implementation class can be used directly
class BBB implements AAA {}Copy the code

The difference between interface default methods and static methods

  1. The default method is called by instance, and the static method is called by interface name
  2. The default methods can be inherited, and the implementation class can either use the interface default methods directly or override them
  3. Static methods cannot be inherited, and implementation classes cannot override interface static methods, which can only be called using the interface name