When we think about the implementation of Java lambda expressions, the first thing we think is: This is Java syntactic sugar, and the implementation must still be anonymous inner classes.

But is this really the case? Don’t worry. Let’s do an experiment. There are several ways to implement a View callback in Android, including anonymous inner classes and lambda expressions. Let’s start with anonymous inner classes:

Next comes the lambda expression:

See the difference? Use an anonymous inner class, using the name of the outer class when using a reference to the outer class. But!! Lambda expressions are used with the name this when referenced by an external class. This shows that implementations of lambda expressions are not anonymous inner classes.

So the question is, if it’s not implemented using anonymous inner classes, how is it implemented? This paper will take advantage of this problem to unlock the mystery of Lambda.

1. Representation of lambda expressions in bytecode

First, as usual, we’ll write a simple Java class as follows:

public class Test { private void startThread() { Runnable r = () -> showToast(); Thread t = new Thread(r); t.start(); } private void showToast() { System.out.println("I am lambda of virtual"); } public static void main(String[] args) { Runnable r = () -> System.out.println("I am lambda of static"); Thread t = new Thread(r); t.start(); Test test = new Test(); test.startThread(); }}Copy the code

After compiling and running, a.class file is generated, and the bytecode is viewed using the Javap tool.

javap -verbose -p Test.class
Copy the code

Where -p means show all classes and members.

When decompiled using Javap, the resulting bytecode is quite long, so let’s just look at the key parts, starting with the main method:

Where we use the lambda call, we generate an Invokedynamic instruction, which, according to the constant pool table above, has symbolic reference #9 and is a CONSTANT_InvokeDynamic_info table type. Review the CONSTANT_InvokeDynamic_info table structure:

project type describe
tag u1 A value of 18
bootstrap_method_attr_index u2 The value must be a valid index to the bootSTRAP_methods [] array of the bootstrap method table in the current Class file
name_and_type_index u2 The value must be a valid index to the current constant pool, and the constant pool entry at that index must be a CONSTANT_NameAndType_info structure representing the method name and method descriptor

Let’s look at constant pool table #9:

From the CONSTANT_InvokeDynamic_info table structure, #1 is the index of BootstrapMethods, corresponding to the BootstrapMethod information of #1:

BootstrapMethod BootstrapMethod

Each instance of an Invokedynamic instruction is called a dynamic call site. A dynamic call site starts with an unlinked state (meaning that the method to be invoked at the point is not specified). Dynamic call points rely on bootstrap methods to link to specific methods. The bootstrap method is generated by the compiler. When the JVM first encounters an InvokeDynamic instruction at runtime, it invokes the bootstrap method to link the invokeDynamic instruction’s name (method name, method signature) to the specific execution code (target method). The return value of the bootstrap method permanently determines the behavior of the calling point. Boot methods return value type is Java lang. Invoke. CallSite, an invokedynamic instruction associated a CallSite, will all the delegate to the CallSite current target (MethodHandle)

The BootstrapMethod is executed only when the invokedynamic instruction is executed during the runtime, so the execution of the lambda expression must also be related to this process. Next, let’s examine the execution of the BootstrapMethod

2. Execute the BootstrapMethod

From # 1 above the corresponding BootstrapMethod known information, this BootstrapMethod invoked, execution is LambdaMetafactory metafactory method, this method is called in CallSite# makeSite method, Called by the JVM. One of the features of invokeDynamic is that once the call point is identified at the first run time, it is bound to that call point, and the binding operation is also in this method

Part code:

The bootstrapMethod. Invoke is direct method calls, here will link to LambdaMetafactory. Metafactory, looked LambdaMetafactory. Metafactory method of annotation:

A quick and simple “method object” constructor implemented as an interface by dispatching a given MethodHandle, which determines the type after passing in the appropriate type and mutable parameters. Typically used as a bootloader for dynamic invocation points executed by InvokeDynamic to support the implementation of lambda expressions in the Java language. This is standard streaming metaFactory, and the altMetafactory method provides additional flexibility support. When the submethod is called and returns the target CallSite, the generated Function Objects is an instance of a class that implements an interface of type invokedType, generates a method with invokedName and the signature given by samMethodType, It is possible to override other methods of the base class as well.

Arguments: Caller: indicates a context that has caller access rights. When used for InvokeDynamic, the virtual machine automatically passes in invokedName: The method that needs to be implemented, when used for InvokeDynamic, is provided by the invokeDynamic structure’s NameAndType attribute, and automatically passed in by the virtual machine to invokedType: The expected signature of the CallSite, the parameter type representing the type of the captured variable (used for the instance of the object to which invokeVirtual belongs), the return type being the interface to be implemented, provided by the invokeDynamic structure’s NameAndType attribute when used for InvokeDynamic, Automatically passed in by the virtual machine, if the implementation method is an instance method and this signature has any parameters, the first parameter in the call signature must correspond to the receiver. SamMethodType: method signature and return type of the method to be implemented implMethod: MethodHandle instantiatedMethodType of the method to be called in the method to be implemented: The method signature and return type of the method to be implemented may be the same as samMethodType, with the return value: the generated dynamic call point

After reading the notes what feeling, feeling more meng forced, this is what ah! Let’s look at the code first

Again in the Test class, let’s look at what the input parameters are when the method is executed, and make a breakpoint inside the method:

The arguments invokeName, invokeType, and samMethodType identify the method of the lambda implementation as an interfaceRunnablethevoid run()Method, and implMethod can be used to obtain information about the method to be called in the run method. In this example, implMethod information is as follows:

Where member is the method signature of the called method, the name here islambda$main$1At this point, the question is, where is this method in the code we wrote? How do I call it? In fact, this method is automatically generated by the compiler, and a similar method is generated at each lambda call point. Inside the method body is the logical implementation of our lambda block. In this case, the methods corresponding to the two lambdas look like this:

Notice that the flags attribute indicates that ACC_SYNTHETIC is generated by the compiler, and that the generated methods are static or not, depending on the specific logic in the lambda block.

New InnerClassLambdaMetafactory statement is used to initialize the class data needed, from the extract method of all kinds of information, we won’t analyze the source code, interested students can write a simple demo debug see, is very interesting. Focus on the logic of buildCallSite

BuildCallSite code:

CallSite buildCallSite () throws LambdaConversionException {/ / by ASM tool to generate the inner Class final Class <? > innerClass = spinInnerClass(); if (invokedType.parameterCount() == 0) { final Constructor<? >[] ctrs = AccessController.doPrivileged( new PrivilegedAction<Constructor<? >[]>() { @Override public Constructor<? >[] run() { Constructor<? >[] ctrs = innerClass.getDeclaredConstructors(); if (ctrs.length == 1) { ctrs[0].setAccessible(true); } return ctrs; }}); if (ctrs.length ! = 1) { throw new LambdaConversionException("Expected one lambda constructor for " + innerClass.getCanonicalName() + ", got " + ctrs.length); } try {// Generate a CallSite instance. Object inst = CTRS [0].newinstance (); return new ConstantCallSite(MethodHandles.constant(samBase, inst)); } catch (ReflectiveOperationException e) { throw new LambdaConversionException("Exception instantiating lambda object", e); } } else { try { UNSAFE.ensureClassInitialized(innerClass); return new ConstantCallSite( MethodHandles.Lookup.IMPL_LOOKUP .findStatic(innerClass, NAME_FACTORY, invokedType)); } catch (ReflectiveOperationException e) { throw new LambdaConversionException("Exception finding constructor", e); }}}Copy the code

The first line of the code, spinInnerClass, generates an inner class that implements the interface indicated by the metaFactory method argument invokedType.

private Class<? > spinInnerClass() throws LambdaConversionException { String[] interfaces; String samIntf = samBase.getName().replace('.', '/'); boolean accidentallySerializable = ! isSerializable && Serializable.class.isAssignableFrom(samBase); if (markerInterfaces.length == 0) { interfaces = new String[]{samIntf}; } else { // Assure no duplicate interfaces (ClassFormatError) Set<String> itfs = new LinkedHashSet<>(markerInterfaces.length + 1); itfs.add(samIntf); for (Class<? > markerInterface : markerInterfaces) { itfs.add(markerInterface.getName().replace('.', '/')); accidentallySerializable |= ! isSerializable && Serializable.class.isAssignableFrom(markerInterface); } interfaces = itfs.toArray(new String[itfs.size()]); } // use the ASM tool's ClassWriter object to generate a class // CLASSFILE_VERSION: Class version number // ACC_SUPER + ACC_FINAL + ACC_SYNTHETIC: access control tag // lambdaClassName: Replace ('.', '/') + "$$Lambda$" + counter. IncrementAndGet () // interfaces: The interface to implement, Get cw.visit(CLASSFILE_VERSION, ACC_SUPER + ACC_FINAL + ACC_SYNTHETIC, from the returnType in invokedType from the mataFactory method. lambdaClassName, null, JAVA_LANG_OBJECT, interfaces); For (int I = 0; i < argDescs.length; i++) { FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_FINAL, argNames[i], argDescs[i], null, null); fv.visitEnd(); } // Generate the constructor generateConstructor(); if (invokedType.parameterCount() ! // generateFactory(); } / / generates the implementation method of the interface MethodVisitor mv = the cw. The visitMethod (ACC_PUBLIC, samMethodName, samMethodType toMethodDescriptorString (), null, null); / / method to add annotations mv. VisitAnnotation (" Ljava/lang/invoke/LambdaForm $Hidden;" , true); / / write lambda blocks the implementation of this case writing a lambda $main $1 here call new ForwardingMethodGenerator (mv). The generate (samMethodType); // Forward the bridges if (additionalBridges ! = null) { for (MethodType mt : additionalBridges) { mv = cw.visitMethod(ACC_PUBLIC|ACC_BRIDGE, samMethodName, mt.toMethodDescriptorString(), null, null); mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;" , true); new ForwardingMethodGenerator(mv).generate(mt); } } if (isSerializable) generateSerializationFriendlyMethods(); else if (accidentallySerializable) generateSerializationHostileMethods(); cw.visitEnd(); // Define the generated class in this VM. final byte[] classBytes = cw.toByteArray(); // If requested, dump out to a file for debugging purposes if (dumper ! = null) { AccessController.doPrivileged(new PrivilegedAction<Void>() { @Override public Void run() { dumper.dumpClass(lambdaClassName, classBytes); return null; } }, null, new FilePermission("<<ALL FILES>>", "read, write"), // createDirectories may need it new PropertyPermission("user.dir", "read")); } return UNSAFE.defineAnonymousClass(targetClass, classBytes, null); }Copy the code

There’s another big topic covered here: ASM, which is a bytecode generation tool that I plan to cover in a later article.

After generating the lambda’s inner class, the buildCallSite method instantiates the class’s object and a CallSite object. The constructor takes type MethodHandle as an input.

MethodHandle is an important part of the java.lang. Invoke package introduced in JDK7. The main purpose of this package is to provide a new mechanism for dynamically determining the target method of a call beyond relying solely on symbolic references. Called a MethodHandle, this approach is similar to C/C++ function Pointers. Some blogs say that InvokeDynamic determines that dynamic invocation uses reflection, which is incorrect.

After the CallSite object is generated, the JVM continues to execute the methods of the object instance of the Class that was just generated by ASM, reaching the goal of the dynamic invocation.

3. View the generated Class code

The above analysis is limited to theoretical reasoning, isn’t it? We need to run a test

Open IDEA and go to Run -> Edit Configurations -> Add VM Options to Add the following configuration:

-Djdk.internal.lambda.dumpProxyClasses=D:\zyl\demo\javademo\JavaTest\src\com\dafasoft\test
Copy the code

The value after the = sign depends on the actual situation, and the purpose of this configuration is to preserve the classes generated by the JVM during runtime

After running the program, there are two more classes:

This is consistent with our analysis above.

4. Conclusion:

Implementation of lambda expressions:

1. When a Java file encounters a lambda expression during compilation, it will extract its internal logical block and encapsulate it in a method named lambda lambda {method}lambda {number}. Whether the method is static depends on the code block and the method

  1. When the JVM encounters an Invokedynamic instruction for the first time, it executes the Bootstrap method, using the Bootstrap method’s input parameters to determine which interface and method to implement.

  2. The Bootstrap method uses ASM to dynamically generate corresponding classes through interfaces and methods and register them with the JVM

  3. The Bootstrap method returns a dynamic CallSite from which the JVM invokes the corresponding method to complete a lambda call.

5 appendix

The complete bytecode generated by test.java compilation:

Classfile /D:/zyl/demo/javademo/JavaTest/out/production/JavaTest/com/dafasoft/test/Test.class
  Last modified 2021-5-11; size 1745 bytes
  MD5 checksum 18b6eb43641fa1295b58d1b1bcacc539
  Compiled from "Test.java"
public class com.dafasoft.test.Test
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #15.#38        // java/lang/Object."<init>":()V
   #2 = InvokeDynamic      #0:#43         // #0:run:(Lcom/dafasoft/test/Test;)Ljava/lang/Runnable;
   #3 = Class              #44            // java/lang/Thread
   #4 = Methodref          #3.#45         // java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
   #5 = Methodref          #3.#46         // java/lang/Thread.start:()V
   #6 = Fieldref           #47.#48        // java/lang/System.out:Ljava/io/PrintStream;
   #7 = String             #49            // I am lambda of virtual
   #8 = Methodref          #50.#51        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #9 = InvokeDynamic      #1:#53         // #1:run:()Ljava/lang/Runnable;
  #10 = Class              #54            // com/dafasoft/test/Test
  #11 = Methodref          #10.#38        // com/dafasoft/test/Test."<init>":()V
  #12 = Methodref          #10.#55        // com/dafasoft/test/Test.startThread:()V
  #13 = String             #56            // I am lambda of static
  #14 = Methodref          #10.#57        // com/dafasoft/test/Test.showToast:()V
  #15 = Class              #58            // java/lang/Object
  #16 = Utf8               <init>
  #17 = Utf8               ()V
  #18 = Utf8               Code
  #19 = Utf8               LineNumberTable
  #20 = Utf8               LocalVariableTable
  #21 = Utf8               this
  #22 = Utf8               Lcom/dafasoft/test/Test;
  #23 = Utf8               startThread
  #24 = Utf8               r
  #25 = Utf8               Ljava/lang/Runnable;
  #26 = Utf8               t
  #27 = Utf8               Ljava/lang/Thread;
  #28 = Utf8               showToast
  #29 = Utf8               main
  #30 = Utf8               ([Ljava/lang/String;)V
  #31 = Utf8               args
  #32 = Utf8               [Ljava/lang/String;
  #33 = Utf8               test
  #34 = Utf8               lambda$main$1
  #35 = Utf8               lambda$startThread$0
  #36 = Utf8               SourceFile
  #37 = Utf8               Test.java
  #38 = NameAndType        #16:#17        // "<init>":()V
  #39 = Utf8               BootstrapMethods
  #40 = MethodHandle       #6:#59         // 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;
  #41 = MethodType         #17            //  ()V
  #42 = MethodHandle       #7:#60         // invokespecial com/dafasoft/test/Test.lambda$startThread$0:()V
  #43 = NameAndType        #61:#62        // run:(Lcom/dafasoft/test/Test;)Ljava/lang/Runnable;
  #44 = Utf8               java/lang/Thread
  #45 = NameAndType        #16:#63        // "<init>":(Ljava/lang/Runnable;)V
  #46 = NameAndType        #64:#17        // start:()V
  #47 = Class              #65            // java/lang/System
  #48 = NameAndType        #66:#67        // out:Ljava/io/PrintStream;
  #49 = Utf8               I am lambda of virtual
  #50 = Class              #68            // java/io/PrintStream
  #51 = NameAndType        #69:#70        // println:(Ljava/lang/String;)V
  #52 = MethodHandle       #6:#71         // invokestatic com/dafasoft/test/Test.lambda$main$1:()V
  #53 = NameAndType        #61:#72        // run:()Ljava/lang/Runnable;
  #54 = Utf8               com/dafasoft/test/Test
  #55 = NameAndType        #23:#17        // startThread:()V
  #56 = Utf8               I am lambda of static
  #57 = NameAndType        #28:#17        // showToast:()V
  #58 = Utf8               java/lang/Object
  #59 = Methodref          #73.#74        // 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;
  #60 = Methodref          #10.#75        // com/dafasoft/test/Test.lambda$startThread$0:()V
  #61 = Utf8               run
  #62 = Utf8               (Lcom/dafasoft/test/Test;)Ljava/lang/Runnable;
  #63 = Utf8               (Ljava/lang/Runnable;)V
  #64 = Utf8               start
  #65 = Utf8               java/lang/System
  #66 = Utf8               out
  #67 = Utf8               Ljava/io/PrintStream;
  #68 = Utf8               java/io/PrintStream
  #69 = Utf8               println
  #70 = Utf8               (Ljava/lang/String;)V
  #71 = Methodref          #10.#76        // com/dafasoft/test/Test.lambda$main$1:()V
  #72 = Utf8               ()Ljava/lang/Runnable;
  #73 = Class              #77            // java/lang/invoke/LambdaMetafactory
  #74 = NameAndType        #78:#82        // 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;
  #75 = NameAndType        #35:#17        // lambda$startThread$0:()V
  #76 = NameAndType        #34:#17        // lambda$main$1:()V
  #77 = Utf8               java/lang/invoke/LambdaMetafactory
  #78 = Utf8               metafactory
  #79 = Class              #84            // java/lang/invoke/MethodHandles$Lookup
  #80 = Utf8               Lookup
  #81 = Utf8               InnerClasses
  #82 = 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;
  #83 = Class              #85            // java/lang/invoke/MethodHandles
  #84 = Utf8               java/lang/invoke/MethodHandles$Lookup
  #85 = Utf8               java/lang/invoke/MethodHandles
{
  public com.dafasoft.test.Test();
    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 5: 0
        line 6: 4
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/dafasoft/test/Test;

  private void startThread();
    descriptor: ()V
    flags: ACC_PRIVATE
    Code:
      stack=3, locals=3, args_size=1
         0: aload_0
         1: invokedynamic #2,  0              // InvokeDynamic #0:run:(Lcom/dafasoft/test/Test;)Ljava/lang/Runnable;
         6: astore_1
         7: new           #3                  // class java/lang/Thread
        10: dup
        11: aload_1
        12: invokespecial #4                  // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
        15: astore_2
        16: aload_2
        17: invokevirtual #5                  // Method java/lang/Thread.start:()V
        20: return
      LineNumberTable:
        line 9: 0
        line 10: 7
        line 11: 16
        line 12: 20
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      21     0  this   Lcom/dafasoft/test/Test;
            7      14     1     r   Ljava/lang/Runnable;
           16       5     2     t   Ljava/lang/Thread;

  private void showToast();
    descriptor: ()V
    flags: ACC_PRIVATE
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #7                  // String I am lambda of virtual
         5: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 15: 0
        line 16: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  this   Lcom/dafasoft/test/Test;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=4, args_size=1
         0: invokedynamic #9,  0              // InvokeDynamic #1:run:()Ljava/lang/Runnable;
         5: astore_1
         6: new           #3                  // class java/lang/Thread
         9: dup
        10: aload_1
        11: invokespecial #4                  // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
        14: astore_2
        15: aload_2
        16: invokevirtual #5                  // Method java/lang/Thread.start:()V
        19: new           #10                 // class com/dafasoft/test/Test
        22: dup
        23: invokespecial #11                 // Method "<init>":()V
        26: astore_3
        27: aload_3
        28: invokespecial #12                 // Method startThread:()V
        31: return
      LineNumberTable:
        line 19: 0
        line 20: 6
        line 21: 15
        line 23: 19
        line 24: 27
        line 25: 31
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      32     0  args   [Ljava/lang/String;
            6      26     1     r   Ljava/lang/Runnable;
           15      17     2     t   Ljava/lang/Thread;
           27       5     3  test   Lcom/dafasoft/test/Test;

  private static void lambda$main$1();
    descriptor: ()V
    flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
    Code:
      stack=2, locals=0, args_size=0
         0: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #13                 // String I am lambda of static
         5: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 19: 0

  private void lambda$startThread$0();
    descriptor: ()V
    flags: ACC_PRIVATE, ACC_SYNTHETIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #14                 // Method showToast:()V
         4: return
      LineNumberTable:
        line 9: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/dafasoft/test/Test;
}
SourceFile: "Test.java"
InnerClasses:
     public static final #80= #79 of #83; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
BootstrapMethods:
  0: #40 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:
      #41 ()V
      #42 invokespecial com/dafasoft/test/Test.lambda$startThread$0:()V
      #41 ()V
  1: #40 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:
      #41 ()V
      #52 invokestatic com/dafasoft/test/Test.lambda$main$1:()V
      #41 ()V
Copy the code