synthetic: a trick of Java cold knowledge compiler

Keywords: Java Lambda JDK jvm

When we read the JDK reflection package source code, we will encounter the isSynthetic() method. One of its implementations is Modifier.isSynthetic(getModifiers()), and the principle of other methods is the same. Modifier is a special class for defining modifiers, among which static final int synchronous = 0x00001000 is one of the modifiers.
I try to decorate my custom class / field / method / constructor with synthetic. But they all fail to compile because they don't know the synthetic modifier. So how does synthetic work?

Official definition

I found the definition in the Java language specification:
Any constructs introduced by the compiler that do not have a corresponding construct in the source code must be marked as synthetic ,except for default constructs and the class initialization method.
If there is no corresponding constructor in the source code for any constructor introduced by the compiler, the code must be marked as synthetic (compound), except for the default constructor and class initialization method.

Verification

Based on the above definition, we know that synthetic is generated by compiler, and synthetic means compound and synthetic. So guess if it's used in inner classes. First, we construct an inner class:

public  class SyntheticDemo {

    public static void main(String[] args) {

        InnerClass innerObject = new InnerClass();
        System.out.println("inner: " + innerObject.inner);
    }

    private static class InnerClass{

        private String inner = "I am inside.";
    }
}

Three files are generated after compilation:

  • SyntheticDemo$1.class
  • SyntheticDemo$InnerClass.class
  • SyntheticDemo.class

Let's decompile these three class files in turn

SyntheticDemo$1.class

javap SyntheticDemo$1.class:

Compiled from "SyntheticDemo.java"
class compiler.SyntheticDemo$1 {
}

javap -v SyntheticDemo$1.class

  Last modified 2019-12-12; size 205 bytes
  MD5 checksum 942560dec8fdbc3d9b65e31e4b41295a
  Compiled from "SyntheticDemo.java"
class compiler.SyntheticDemo$1
  minor version: 0
  major version: 52
  flags: ACC_SUPER, ACC_SYNTHETIC
Constant pool:
   #1 = Class              #7             // compiler/SyntheticDemo$1
   #2 = Class              #9             // java/lang/Object
   #3 = Utf8               SourceFile
   #4 = Utf8               SyntheticDemo.java
   #5 = Utf8               EnclosingMethod
   #6 = Class              #10            // compiler/SyntheticDemo
   #7 = Utf8               compiler/SyntheticDemo$1
   #8 = Utf8               InnerClasses
   #9 = Utf8               java/lang/Object
  #10 = Utf8               compiler/SyntheticDemo
{
}
SourceFile: "SyntheticDemo.java"
EnclosingMethod: #6.#0                  // compiler.SyntheticDemo
InnerClasses:
     static #1; //class compiler/SyntheticDemo$1

There is nothing in this class, and the flags of this class contain ACC ﹣ synchronous.

SyntheticDemo$InnerClass.class

javap SyntheticDemo$InnerClass.class:

Compiled from "SyntheticDemo.java"
class compiler.SyntheticDemo$InnerClass {
  compiler.SyntheticDemo$InnerClass(compiler.SyntheticDemo$1);
  static java.lang.String access$100(compiler.SyntheticDemo$InnerClass);
}

javap SyntheticDemo$InnerClass.class:

Classfile /D:/idea_workspace/mycode/target/classes/compiler/SyntheticDemo$InnerClass.class
  Last modified 2019-12-12; size 763 bytes
  MD5 checksum f13a4310236918575d26a2909aa8507e
  Compiled from "SyntheticDemo.java"
class compiler.SyntheticDemo$InnerClass
  minor version: 0
  major version: 52
  flags: ACC_SUPER
Constant pool:
   #1 = Fieldref           #5.#26         // compiler/SyntheticDemo$InnerClass.inner:Ljava/lang/String;
   #2 = Methodref          #5.#27         // compiler/SyntheticDemo$InnerClass."<init>":()V
   #3 = Methodref          #6.#27         // java/lang/Object."<init>":()V
   #4 = String             #28 / / I'm inside
   #5 = Class              #30            // compiler/SyntheticDemo$InnerClass
   #6 = Class              #31            // java/lang/Object
   #7 = Utf8               inner
   #8 = Utf8               Ljava/lang/String;
   #9 = Utf8               <init>
  #10 = Utf8               ()V
  #11 = Utf8               Code
  #12 = Utf8               LineNumberTable
  #13 = Utf8               LocalVariableTable
  #14 = Utf8               this
  #15 = Utf8               InnerClass
  #16 = Utf8               InnerClasses
  #17 = Utf8               Lcompiler/SyntheticDemo$InnerClass;
  #18 = Class              #32            // compiler/SyntheticDemo$1
  #19 = Utf8               (Lcompiler/SyntheticDemo$1;)V
  #20 = Utf8               x0
  #21 = Utf8               Lcompiler/SyntheticDemo$1;
  #22 = Utf8               access$100
  #23 = Utf8               (Lcompiler/SyntheticDemo$InnerClass;)Ljava/lang/String;
  #24 = Utf8               SourceFile
  #25 = Utf8               SyntheticDemo.java
  #26 = NameAndType        #7:#8          // inner:Ljava/lang/String;
  #27 = NameAndType        #9:#10         // "<init>":()V
  #28 = Utf8 I'm inside
  #29 = Class              #33            // compiler/SyntheticDemo
  #30 = Utf8               compiler/SyntheticDemo$InnerClass
  #31 = Utf8               java/lang/Object
  #32 = Utf8               compiler/SyntheticDemo$1
  #33 = Utf8               compiler/SyntheticDemo
{
  compiler.SyntheticDemo$InnerClass(compiler.SyntheticDemo$1);
    descriptor: (Lcompiler/SyntheticDemo$1;)V
    flags: ACC_SYNTHETIC
    Code:
      stack=1, locals=2, args_size=2
         0: aload_0
         1: invokespecial #2                  // Method "<init>":()V
         4: return
      LineNumberTable:
        line 18: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcompiler/SyntheticDemo$InnerClass;
            0       5     1    x0   Lcompiler/SyntheticDemo$1;

  static java.lang.String access$100(compiler.SyntheticDemo$InnerClass);
    descriptor: (Lcompiler/SyntheticDemo$InnerClass;)Ljava/lang/String;
    flags: ACC_STATIC, ACC_SYNTHETIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #1                  // Field inner:Ljava/lang/String;
         4: areturn
      LineNumberTable:
        line 18: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0    x0   Lcompiler/SyntheticDemo$InnerClass;
}
SourceFile: "SyntheticDemo.java"
InnerClasses:
     static #18; //class compiler/SyntheticDemo$1

SyntheticDemo$InnerClass contains the constructor and access0 method with default access. And the constructor and method are synthetic. The constructor's argument is synthetic demo $1. The logic of access0 is to get private properties of internal classes.

SyntheticDemo.class

Take a look at the last class, javap synthetic demo.class:

Compiled from "SyntheticDemo.java"
public class compiler.SyntheticDemo {
  public compiler.SyntheticDemo();
  public static void main(java.lang.String[]);
}

javap -v SyntheticDemo.class

Classfile /D:/idea_workspace/mycode/target/classes/compiler/SyntheticDemo.class
  Last modified 2019-12-12; size 1040 bytes
  MD5 checksum 7519ad7d2df356217e19642ed0a06ee4
  Compiled from "SyntheticDemo.java"
public class compiler.SyntheticDemo
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #13.#32        // java/lang/Object."<init>":()V
   #2 = Class              #33            // compiler/SyntheticDemo$InnerClass
   #3 = Methodref          #2.#34         // compiler/SyntheticDemo$InnerClass."<init>":(Lcompiler/SyntheticDemo$1;)V
   #4 = Fieldref           #35.#36        // java/lang/System.out:Ljava/io/PrintStream;
   #5 = Class              #37            // java/lang/StringBuilder
   #6 = Methodref          #5.#32         // java/lang/StringBuilder."<init>":()V
   #7 = String             #38            // inner:
   #8 = Methodref          #5.#39         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   #9 = Methodref          #2.#40         // compiler/SyntheticDemo$InnerClass.access$100:(Lcompiler/SyntheticDemo$InnerClass;)Ljava/lang/String;
  #10 = Methodref          #5.#41         // java/lang/StringBuilder.toString:()Ljava/lang/String;
  #11 = Methodref          #42.#43        // java/io/PrintStream.println:(Ljava/lang/String;)V
  #12 = Class              #44            // compiler/SyntheticDemo
  #13 = Class              #45            // java/lang/Object
  #14 = Class              #46            // compiler/SyntheticDemo$1
  #15 = Utf8               InnerClasses
  #16 = Utf8               InnerClass
  #17 = Utf8               <init>
  #18 = Utf8               ()V
  #19 = Utf8               Code
  #20 = Utf8               LineNumberTable
  #21 = Utf8               LocalVariableTable
  #22 = Utf8               this
  #23 = Utf8               Lcompiler/SyntheticDemo;
  #24 = Utf8               main
  #25 = Utf8               ([Ljava/lang/String;)V
  #26 = Utf8               args
  #27 = Utf8               [Ljava/lang/String;
  #28 = Utf8               innerObject
  #29 = Utf8               Lcompiler/SyntheticDemo$InnerClass;
  #30 = Utf8               SourceFile
  #31 = Utf8               SyntheticDemo.java
  #32 = NameAndType        #17:#18        // "<init>":()V
  #33 = Utf8               compiler/SyntheticDemo$InnerClass
  #34 = NameAndType        #17:#47        // "<init>":(Lcompiler/SyntheticDemo$1;)V
  #35 = Class              #48            // java/lang/System
  #36 = NameAndType        #49:#50        // out:Ljava/io/PrintStream;
  #37 = Utf8               java/lang/StringBuilder
  #38 = Utf8               inner:
  #39 = NameAndType        #51:#52        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #40 = NameAndType        #53:#54        // access$100:(Lcompiler/SyntheticDemo$InnerClass;)Ljava/lang/String;
  #41 = NameAndType        #55:#56        // toString:()Ljava/lang/String;
  #42 = Class              #57            // java/io/PrintStream
  #43 = NameAndType        #58:#59        // println:(Ljava/lang/String;)V
  #44 = Utf8               compiler/SyntheticDemo
  #45 = Utf8               java/lang/Object
  #46 = Utf8               compiler/SyntheticDemo$1
  #47 = Utf8               (Lcompiler/SyntheticDemo$1;)V
  #48 = Utf8               java/lang/System
  #49 = Utf8               out
  #50 = Utf8               Ljava/io/PrintStream;
  #51 = Utf8               append
  #52 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #53 = Utf8               access$100
  #54 = Utf8               (Lcompiler/SyntheticDemo$InnerClass;)Ljava/lang/String;
  #55 = Utf8               toString
  #56 = Utf8               ()Ljava/lang/String;
  #57 = Utf8               java/io/PrintStream
  #58 = Utf8               println
  #59 = Utf8               (Ljava/lang/String;)V
{
  public compiler.SyntheticDemo();
    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   Lcompiler/SyntheticDemo;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=2, args_size=1
         0: new           #2                  // class compiler/SyntheticDemo$InnerClass
         3: dup
         4: aconst_null
         5: invokespecial #3                  // Method compiler/SyntheticDemo$InnerClass."<init>":(Lcompiler/SyntheticDemo$1;)V
         8: astore_1
         9: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
        12: new           #5                  // class java/lang/StringBuilder
        15: dup
        16: invokespecial #6                  // Method java/lang/StringBuilder."<init>":()V
        19: ldc           #7                  // String inner:
        21: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        24: aload_1
        25: invokestatic  #9                  // Method compiler/SyntheticDemo$InnerClass.access$100:(Lcompiler/SyntheticDemo$InnerClass;)Ljava/lang/String;
        28: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        31: invokevirtual #10                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        34: invokevirtual #11                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        37: return
      LineNumberTable:
        line 14: 0
        line 15: 9
        line 16: 37
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      38     0  args   [Ljava/lang/String;
            9      29     1 innerObject   Lcompiler/SyntheticDemo$InnerClass;
}
SourceFile: "SyntheticDemo.java"
InnerClasses:
     static #14; //class compiler/SyntheticDemo$1

Here, all the puzzles have been solved. In the new inner class, the compiler generated constructor is called and a null is passed as the parameter:

         5: invokespecial #3                  // Method compiler/SyntheticDemo$InnerClass."<init>":(Lcompiler/SyntheticDemo$1;)V
         8: astore_1

When obtaining the private property of an internal class, the compiled access0 method called:

 invokestatic  #9                  // Method compiler/SyntheticDemo$InnerClass.access$100:(Lcompiler/SyntheticDemo$InnerClass;)Ljava/lang/String;
Answer

Because the inner class is private, the default constructor will also be private, so the outer class cannot call the default constructor. We know that external classes in Java language can construct and access internal classes. Its implementation is a trick played by the compiler. The compiler generates some constructors, classes and methods to help us connect the private parts of external classes and internal classes. The constructors, methods and classes generated by such compilers are syntactic tags. That is to say, this feature is given by the java compiler, while other languages running on the JVM do not necessarily have this feature. It depends on whether the corresponding compiler also uses some tricks to connect.

If we remove the private keyword of the inner domain of the InnerClass, and then look up the bytecode, we can find that the access0 of the internal class is gone, because the external class can directly access the domain of package access permission of the internal class, of course, there is no need to generate access0 to bridge. Readers can try it on their own.
If we remove the inner class private of InnerClass, or add a non private inner class constructor instead of removing it, then look at the bytecode and find that the SyntheticDemo .class disappears, and the compiler does not generate the package permission constructor with the SyntheticDemo .class parameter, because it is not needed. Readers can experiment on their own.

extend
Anonymous Inner Class

Is synthetic used in anonymous inner classes?

public  class SyntheticDemo2 {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("11");
            }
        }).start();
    }
}

The above code will also generate a synthetic demo2 $1. Class. Let's look at the bytecode:

Classfile /D:/idea_workspace/mycode/target/classes/compiler/SyntheticDemo2$1.class
  Last modified 2019-12-12; size 503 bytes
  MD5 checksum 207c9ba26f66c2fbfd5d092c7f9f844e
  Compiled from "SyntheticDemo2.java"
final class compiler.SyntheticDemo2$1 implements java.lang.Runnable
  minor version: 0
  major version: 52
  flags: ACC_FINAL, ACC_SUPER
Constant pool:
   #1 = Methodref          #3.#19         // java/lang/Object."<init>":()V
   #2 = Class              #20            // compiler/SyntheticDemo2$1
   #3 = Class              #21            // java/lang/Object
   #4 = Class              #22            // java/lang/Runnable
   #5 = Utf8               <init>
   #6 = Utf8               ()V
   #7 = Utf8               Code
   #8 = Utf8               LineNumberTable
   #9 = Utf8               LocalVariableTable
  #10 = Utf8               this
  #11 = Utf8               InnerClasses
  #12 = Utf8               Lcompiler/SyntheticDemo2$1;
  #13 = Utf8               run
  #14 = Utf8               SourceFile
  #15 = Utf8               SyntheticDemo2.java
  #16 = Utf8               EnclosingMethod
  #17 = Class              #23            // compiler/SyntheticDemo2
  #18 = NameAndType        #24:#25        // main:([Ljava/lang/String;)V
  #19 = NameAndType        #5:#6          // "<init>":()V
  #20 = Utf8               compiler/SyntheticDemo2$1
  #21 = Utf8               java/lang/Object
  #22 = Utf8               java/lang/Runnable
  #23 = Utf8               compiler/SyntheticDemo2
  #24 = Utf8               main
  #25 = Utf8               ([Ljava/lang/String;)V
{
  compiler.SyntheticDemo2$1();
    descriptor: ()V
    flags:
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 13: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcompiler/SyntheticDemo2$1;

  public void run();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=0, locals=1, args_size=1
         0: return
      LineNumberTable:
        line 17: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       1     0  this   Lcompiler/SyntheticDemo2$1;
}
SourceFile: "SyntheticDemo2.java"
EnclosingMethod: #17.#18                // compiler.SyntheticDemo2.main
InnerClasses:
     static #2; //class compiler/SyntheticDemo2$1

There is no ACC [synchronous] mark in the bytecode, indicating that it is not related to synchronous.

lambda expression
class SyntheticDemo3 {

    public static void main(String[] args) {
        new Thread(()->{}).start();
    }

}

Only one synthetic demo3.class
Its bytecode:

Classfile /D:/idea_workspace/mycode/target/classes/compiler/SyntheticDemo3.class
  Last modified 2019-12-12; size 1042 bytes
  MD5 checksum 50722aaf911c908da83df897f5141c05
  Compiled from "SyntheticDemo3.java"
class compiler.SyntheticDemo3
  minor version: 0
  major version: 52
  flags: ACC_SUPER
Constant pool:
   #1 = Methodref          #7.#22         // java/lang/Object."<init>":()V
   #2 = Class              #23            // java/lang/Thread
   #3 = InvokeDynamic      #0:#28         // #0:run:()Ljava/lang/Runnable;
   #4 = Methodref          #2.#29         // java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
   #5 = Methodref          #2.#30         // java/lang/Thread.start:()V
   #6 = Class              #31            // compiler/SyntheticDemo3
   #7 = Class              #32            // java/lang/Object
   #8 = Utf8               <init>
   #9 = Utf8               ()V
  #10 = Utf8               Code
  #11 = Utf8               LineNumberTable
  #12 = Utf8               LocalVariableTable
  #13 = Utf8               this
  #14 = Utf8               Lcompiler/SyntheticDemo3;
  #15 = Utf8               main
  #16 = Utf8               ([Ljava/lang/String;)V
  #17 = Utf8               args
  #18 = Utf8               [Ljava/lang/String;
  #19 = Utf8               lambda$main$0
  #20 = Utf8               SourceFile
  #21 = Utf8               SyntheticDemo3.java
  #22 = NameAndType        #8:#9          // "<init>":()V
  #23 = Utf8               java/lang/Thread
  #24 = Utf8               BootstrapMethods
  #25 = MethodHandle       #6:#33         // 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;
  #26 = MethodType         #9             //  ()V
  #27 = MethodHandle       #6:#34         // invokestatic compiler/SyntheticDemo3.lambda$main$0:()V
  #28 = NameAndType        #35:#36        // run:()Ljava/lang/Runnable;
  #29 = NameAndType        #8:#37         // "<init>":(Ljava/lang/Runnable;)V
  #30 = NameAndType        #38:#9         // start:()V
  #31 = Utf8               compiler/SyntheticDemo3
  #32 = Utf8               java/lang/Object
  #33 = Methodref          #39.#40        // java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;L
java/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #34 = Methodref          #6.#41         // compiler/SyntheticDemo3.lambda$main$0:()V
  #35 = Utf8               run
  #36 = Utf8               ()Ljava/lang/Runnable;
  #37 = Utf8               (Ljava/lang/Runnable;)V
  #38 = Utf8               start
  #39 = Class              #42            // java/lang/invoke/LambdaMetafactory
  #40 = NameAndType        #43:#47        // metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/l
ang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #41 = NameAndType        #19:#9         // lambda$main$0:()V
  #42 = Utf8               java/lang/invoke/LambdaMetafactory
  #43 = Utf8               metafactory
  #44 = Class              #49            // java/lang/invoke/MethodHandles$Lookup
  #45 = Utf8               Lookup
  #46 = Utf8               InnerClasses
  #47 = 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;
  #48 = Class              #50            // java/lang/invoke/MethodHandles
  #49 = Utf8               java/lang/invoke/MethodHandles$Lookup
  #50 = Utf8               java/lang/invoke/MethodHandles
{
  compiler.SyntheticDemo3();
    descriptor: ()V
    flags:
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 8: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcompiler/SyntheticDemo3;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=1, args_size=1
         0: new           #2                  // class java/lang/Thread
         3: dup
         4: invokedynamic #3,  0              // InvokeDynamic #0:run:()Ljava/lang/Runnable;
         9: invokespecial #4                  // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
        12: invokevirtual #5                  // Method java/lang/Thread.start:()V
        15: return
      LineNumberTable:
        line 11: 0
        line 12: 15
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      16     0  args   [Ljava/lang/String;
}
SourceFile: "SyntheticDemo3.java"
InnerClasses:
     public static final #45= #44 of #48; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
BootstrapMethods:
  0: #25 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/Method
Type;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
    Method arguments:
      #26 ()V
      #27 invokestatic compiler/SyntheticDemo3.lambda$main$0:()V
      #26 ()V

lambda expressions are not implemented by inner classes, but by invokedynamic. Have a chance to write an article about invokedynamic.

For more Java articles, please pay attention to the public address:

Posted by Tyche on Thu, 12 Dec 2019 00:17:29 -0800