首页 > 其他分享 >深入理解Lambda表达式

深入理解Lambda表达式

时间:2022-10-09 16:11:40浏览次数:66  
标签:lang Ljava java String invoke Utf8 深入 表达式 Lambda

Lambda表达式初体验

简介

Lambda 表达式(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。Lambda表达式可以表示闭包(注意和数学传统意义上的不同)。
——《百度百科》

Java对于Lambda表达式的支持是从JDK8开始的,它来源于数学中的λ演算,是一套关于函数\(f(x)\)定义、输入量、输出量的计算方案,简化了匿名函数的编写,使代码变得简洁。

举个例子

首先声明一个接口Factory和一个User类。

public interface Factory {
    Object getObject();
}

public class User {
    private String name;
    private int age;

    public User() {}
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "User{" +
            "name='" + name + '\'' +
            ", age=" + age +
            '}';
    }
}

按照传统的方式,想要实现Factory接口中的抽象方法,有两种方法:

  • 子类实现接口
  • 匿名内部类

对于第一种方式,代码如下:

public class SubClass implements Factory {
    @Override
    public Object getObject() {
        return new User("tom", 20);
    }
}
public class Code01_LambdaTest {
    public static void main(String[] args) {
        // 子类实现接口
        Factory factory = new SubClass();
        System.out.println("user = " + user);
    }
}

对于第二种方式,代码如下:

public class Code01_LambdaTest {
    public static void main(String[] args) {
        // 匿名内部类
        Factory factory = new Factory() {
            @Override
            public Object getObject() {
                return new User("John", 18);
            }
        };
        System.out.println("user = " + user);
    }
}

分析上面的代码,发现接口Factory中仅仅只有一个抽象方法,我们的目的是为了获取User类的对象这么一个简单的操作,却需要书写这么多代码,最最核心的其实就是这一句return new User("John", 18),有没有更加简洁的书写方式实现上面的需求呢?
第三种方法,就是我们要介绍的Lambda表达式。请看代码:

public class Code01_LambdaTest {
    public static void main(String[] args) {
        // lambda表达式
        Factory factory = () -> new User("Mike", 30);
        System.out.println("user = " + user);
    }
}

通过对比就会发现,代码是多么的简洁高效!

Lambda表达式的语法格式

Lambda表达式应用举例

Lambda的底层实现

它的本质是函数式接口的匿名子类的匿名对象

import java.util.Arrays;
import java.util.List;

public class LambdaPrinciple {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("I", "Love", "You");
        list.forEach(s -> System.out.println(s));
    }
}

编译后的字节码文件
Snipaste_2022-10-09_14-01-27.png
执行反编译命令:java -jar cfr-0.145.jar LambdaPrinciple.class --decodelambdas false
2.png

import java.io.PrintStream;
import java.lang.invoke.LambdaMetafactory;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;

public class LambdaPrinciple {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("I", "Love", "You");
        list.forEach((Consumer<String>) LambdaMetafactory.metafactory(
            null, null, null, 
            (Ljava / lang / Object;)V, 
            lambda$main$0(java.lang.String), 
            (Ljava / lang / String;)V)());
    }

    private static /* synthetic */ void lambda$main$0(String s) {
        System.out.println(s);
    }
}

从上面可以看到,Lambda表达式最终被编译成lambda$main$0静态方法去执行。
接下来找到LambdaMetafactory.java,看下它的metafactory方法

public final class LambdaMetafactory {
    ...
    // LambdaMetafactory bootstrap methods are startup sensitive, and may be
    // special cased in java.lang.invoke.BootstrapMethodInvoker to ensure
    // methods are invoked with exact type information to avoid generating
    // code for runtime checks. Take care any changes or additions here are
    // reflected there as appropriate.

    /**
     * Facilitates the creation of simple "function objects" that implement one
     * or more interfaces by delegation to a provided {@link MethodHandle},
     * after appropriate type adaptation and partial evaluation of arguments.
     * Typically used as a <em>bootstrap method</em> for {@code invokedynamic}
     * call sites, to support the <em>lambda expression</em> and <em>method
     * reference expression</em> features of the Java Programming Language.
     *
     * <p>This is the standard, streamlined metafactory; additional flexibility
     * is provided by {@link #altMetafactory(MethodHandles.Lookup, String, MethodType, Object...)}.
     * A general description of the behavior of this method is provided
     * {@link LambdaMetafactory above}.
     *
     * <p>When the target of the {@code CallSite} returned from this method is
     * invoked, the resulting function objects are instances of a class which
     * implements the interface named by the return type of {@code factoryType},
     * declares a method with the name given by {@code interfaceMethodName} and the
     * signature given by {@code interfaceMethodType}.  It may also override additional
     * methods from {@code Object}.
     *
     * @param caller Represents a lookup context with the accessibility
     *               privileges of the caller.  Specifically, the lookup context
     *               must have {@linkplain MethodHandles.Lookup#hasFullPrivilegeAccess()
     *               full privilege access}.
     *               When used with {@code invokedynamic}, this is stacked
     *               automatically by the VM.
     * @param interfaceMethodName The name of the method to implement.  When used with
     *                            {@code invokedynamic}, this is provided by the
     *                            {@code NameAndType} of the {@code InvokeDynamic}
     *                            structure and is stacked automatically by the VM.
     * @param factoryType The expected signature of the {@code CallSite}.  The
     *                    parameter types represent the types of capture variables;
     *                    the return type is the interface to implement.   When
     *                    used with {@code invokedynamic}, this is provided by
     *                    the {@code NameAndType} of the {@code InvokeDynamic}
     *                    structure and is stacked automatically by the VM.
     * @param interfaceMethodType Signature and return type of method to be
     *                            implemented by the function object.
     * @param implementation A direct method handle describing the implementation
     *                       method which should be called (with suitable adaptation
     *                       of argument types and return types, and with captured
     *                       arguments prepended to the invocation arguments) at
     *                       invocation time.
     * @param dynamicMethodType The signature and return type that should
     *                          be enforced dynamically at invocation time.
     *                          In simple use cases this is the same as
     *                          {@code interfaceMethodType}.
     * @return a CallSite whose target can be used to perform capture, generating
     *         instances of the interface named by {@code factoryType}
     * @throws LambdaConversionException If {@code caller} does not have full privilege
     *         access, or if {@code interfaceMethodName} is not a valid JVM
     *         method name, or if the return type of {@code factoryType} is not
     *         an interface, or if {@code implementation} is not a direct method
     *         handle referencing a method or constructor, or if the linkage
     *         invariants are violated, as defined {@link LambdaMetafactory above}.
     * @throws NullPointerException If any argument is {@code null}.
     * @throws SecurityException If a security manager is present, and it
     *         <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
     *         from {@code caller} to the package of {@code implementation}.
     */
    public static CallSite metafactory(MethodHandles.Lookup caller,
                                       String interfaceMethodName,
                                       MethodType factoryType,
                                       MethodType interfaceMethodType,
                                       MethodHandle implementation,
                                       MethodType dynamicMethodType)
    throws LambdaConversionException {
        AbstractValidatingLambdaMetafactory mf;
        mf = new InnerClassLambdaMetafactory(Objects.requireNonNull(caller),
                                             Objects.requireNonNull(factoryType),
                                             Objects.requireNonNull(interfaceMethodName),
                                             Objects.requireNonNull(interfaceMethodType),
                                             Objects.requireNonNull(implementation),
                                             Objects.requireNonNull(dynamicMethodType),
                                             false,
                                             EMPTY_CLASS_ARRAY,
                                             EMPTY_MT_ARRAY);
        mf.validateMetafactoryArgs();
        return mf.buildCallSite();
    }
}

metafactory方法参数的说明:

接下来看下new InnerClassLambdaMetafactory()的代码,它的作用是创建内部类

public InnerClassLambdaMetafactory(MethodHandles.Lookup caller,
                                   MethodType factoryType,
                                   String interfaceMethodName,
                                   MethodType interfaceMethodType,
                                   MethodHandle implementation,
                                   MethodType dynamicMethodType,
                                   boolean isSerializable,
                                   Class<?>[] altInterfaces,
                                   MethodType[] altMethods)
    throws LambdaConversionException {
    super(caller, factoryType, interfaceMethodName, interfaceMethodType,
        implementation, dynamicMethodType,
        isSerializable, altInterfaces, altMethods);
    implMethodClassName = implClass.getName().replace('.', '/');
    implMethodName = implInfo.getName();
    implMethodDesc = implInfo.getMethodType().toMethodDescriptorString();
    constructorType = factoryType.changeReturnType(Void.TYPE);
    lambdaClassName = lambdaClassName(targetClass);
    // If the target class invokes a protected method inherited from a
    // superclass in a different package, or does 'invokespecial', the
    // lambda class has no access to the resolved method. Instead, we need
    // to pass the live implementation method handle to the proxy class
    // to invoke directly. (javac prefers to avoid this situation by
    // generating bridges in the target class)
    useImplMethodHandle = (Modifier.isProtected(implInfo.getModifiers()) &&
                           !VerifyAccess.isSamePackage(targetClass, implInfo.getDeclaringClass())) ||
        implKind == H_INVOKESPECIAL;
    cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
    int parameterCount = factoryType.parameterCount();
    if (parameterCount > 0) {
        argNames = new String[parameterCount];
        argDescs = new String[parameterCount];
        for (int i = 0; i < parameterCount; i++) {
            argNames[i] = "arg$" + (i + 1);
            argDescs[i] = BytecodeDescriptor.unparse(factoryType.parameterType(i));
        }
    } else {
        argNames = argDescs = EMPTY_STRING_ARRAY;
    }
}

值得注意的是cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);,它的作用是构造一个新的ClassWriter对象,写字节码文件,这其实就是ASM技术

final String dumpProxyClassesKey = "jdk.internal.lambda.dumpProxyClasses";

final class LambdaPrinciple$$Lambda$1 implements Consumer {
    private LambdaPrinciple$$Lambda$1() {
    }

    @LambdaForm.Hidden
    public void accept(Object object) {
        LambdaPrinciple.lambda$main$0((String)object);
    }
}

使用Java反编译命令javap -p -v .\LambdaPrinciple

 Last modified 2022-10-9; size 1566 bytes
  MD5 checksum 2c03c5e9129bf4b9bdd72edde1fc98ff
  Compiled from "LambdaPrinciple.java"
public class class04_lambda_principle.LambdaPrinciple
  minor version: 0
  major version: 61
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #2.#3          // java/lang/Object."<init>":()V
   #2 = Class              #4             // java/lang/Object
   #3 = NameAndType        #5:#6          // "<init>":()V
   #4 = Utf8               java/lang/Object
   #5 = Utf8               <init>
   #6 = Utf8               ()V
   #7 = Class              #8             // java/lang/String
   #8 = Utf8               java/lang/String
   #9 = String             #10            // I
  #10 = Utf8               I
  #11 = String             #12            // Love
  #12 = Utf8               Love
  #13 = String             #14            // You
  #14 = Utf8               You
  #15 = Methodref          #16.#17        // java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;
  #16 = Class              #18            // java/util/Arrays
  #17 = NameAndType        #19:#20        // asList:([Ljava/lang/Object;)Ljava/util/List;
  #18 = Utf8               java/util/Arrays
  #19 = Utf8               asList
  #20 = Utf8               ([Ljava/lang/Object;)Ljava/util/List;
  #21 = InvokeDynamic      #0:#22         // #0:accept:()Ljava/util/function/Consumer;
  #22 = NameAndType        #23:#24        // accept:()Ljava/util/function/Consumer;
  #23 = Utf8               accept
  #24 = Utf8               ()Ljava/util/function/Consumer;
  #25 = InterfaceMethodref #26.#27        // java/util/List.forEach:(Ljava/util/function/Consumer;)V
  #26 = Class              #28            // java/util/List
  #27 = NameAndType        #29:#30        // forEach:(Ljava/util/function/Consumer;)V
  #28 = Utf8               java/util/List
  #29 = Utf8               forEach
  #30 = Utf8               (Ljava/util/function/Consumer;)V
  #31 = Fieldref           #32.#33        // java/lang/System.out:Ljava/io/PrintStream;
  #32 = Class              #34            // java/lang/System
  #33 = NameAndType        #35:#36        // out:Ljava/io/PrintStream;
  #34 = Utf8               java/lang/System
  #35 = Utf8               out
  #36 = Utf8               Ljava/io/PrintStream;
  #37 = Methodref          #38.#39        // java/io/PrintStream.println:(Ljava/lang/String;)V
  #38 = Class              #40            // java/io/PrintStream
  #39 = NameAndType        #41:#42        // println:(Ljava/lang/String;)V
  #40 = Utf8               java/io/PrintStream
  #41 = Utf8               println
  #42 = Utf8               (Ljava/lang/String;)V
  #43 = Class              #44            // class04_lambda_principle/LambdaPrinciple
  #44 = Utf8               class04_lambda_principle/LambdaPrinciple
  #45 = Utf8               Code
  #46 = Utf8               LineNumberTable
  #47 = Utf8               LocalVariableTable
  #48 = Utf8               this
  #49 = Utf8               Lclass04_lambda_principle/LambdaPrinciple;
  #50 = Utf8               main
  #51 = Utf8               ([Ljava/lang/String;)V
  #52 = Utf8               args
  #53 = Utf8               [Ljava/lang/String;
  #54 = Utf8               list
  #55 = Utf8               Ljava/util/List;
  #56 = Utf8               LocalVariableTypeTable
  #57 = Utf8               Ljava/util/List<Ljava/lang/String;>;
  #58 = Utf8               lambda$main$0
  #59 = Utf8               s
  #60 = Utf8               Ljava/lang/String;
  #61 = Utf8               SourceFile
  #62 = Utf8               LambdaPrinciple.java
  #63 = Utf8               BootstrapMethods
  #64 = MethodHandle       #6:#65         // 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;
  #65 = Methodref          #66.#67        // 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;
  #66 = Class              #68            // java/lang/invoke/LambdaMetafactory
  #67 = NameAndType        #69:#70        // 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;
  #68 = Utf8               java/lang/invoke/LambdaMetafactory
  #69 = Utf8               metafactory
  #70 = Utf8               (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;
  #71 = MethodType         #72            //  (Ljava/lang/Object;)V
  #72 = Utf8               (Ljava/lang/Object;)V
  #73 = MethodHandle       #6:#74         // invokestatic class04_lambda_principle/LambdaPrinciple.lambda$main$0:(Ljava/lang/String;)V
  #74 = Methodref          #43.#75        // class04_lambda_principle/LambdaPrinciple.lambda$main$0:(Ljava/lang/String;)V
  #75 = NameAndType        #58:#42        // lambda$main$0:(Ljava/lang/String;)V
  #76 = MethodType         #42            //  (Ljava/lang/String;)V
  #77 = Utf8               InnerClasses
  #78 = Class              #79            // java/lang/invoke/MethodHandles$Lookup
  #79 = Utf8               java/lang/invoke/MethodHandles$Lookup
  #80 = Class              #81            // java/lang/invoke/MethodHandles
  #81 = Utf8               java/lang/invoke/MethodHandles
  #82 = Utf8               Lookup
{
  public class04_lambda_principle.LambdaPrinciple();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 10: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lclass04_lambda_principle/LambdaPrinciple;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=4, locals=2, args_size=1
         0: iconst_3
         1: anewarray     #7                  // class java/lang/String
         4: dup
         5: iconst_0
         6: ldc           #9                  // String I
         8: aastore
         9: dup
        10: iconst_1
        11: ldc           #11                 // String Love
        13: aastore
        14: dup
        15: iconst_2
        16: ldc           #13                 // String You
        18: aastore
        19: invokestatic  #15                 // Method java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;
        22: astore_1
        23: aload_1
        24: invokedynamic #21,  0             // InvokeDynamic #0:accept:()Ljava/util/function/Consumer;
        29: invokeinterface #25,  2           // InterfaceMethod java/util/List.forEach:(Ljava/util/function/Consumer;)V
        34: return
      LineNumberTable:
        line 12: 0
        line 13: 23
        line 14: 34
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      35     0  args   [Ljava/lang/String;
           23      12     1  list   Ljava/util/List;
      LocalVariableTypeTable:
        Start  Length  Slot  Name   Signature
           23      12     1  list   Ljava/util/List<Ljava/lang/String;>;

  private static void lambda$main$0(java.lang.String);
    descriptor: (Ljava/lang/String;)V
    flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #31                 // Field java/lang/System.out:Ljava/io/PrintStream;
         3: aload_0
         4: invokevirtual #37                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         7: return
      LineNumberTable:
        line 13: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       8     0     s   Ljava/lang/String;
}
SourceFile: "LambdaPrinciple.java"
BootstrapMethods:
  0: #64 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;
    Method arguments:
      #71 (Ljava/lang/Object;)V
      #73 invokestatic class04_lambda_principle/LambdaPrinciple.lambda$main$0:(Ljava/lang/String;)V
      #76 (Ljava/lang/String;)V
InnerClasses:
     public static final #82= #78 of #80; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles

Java7开始支持动态编译
3.png

标签:lang,Ljava,java,String,invoke,Utf8,深入,表达式,Lambda
From: https://www.cnblogs.com/aldalee/p/16772505.html

相关文章