简体   繁体   中英

How does Synthetic Fields within the Method Local Inner Classes are being linked to the values of the Method Local Variables?

In Java, method local variables live on the Stack as long as the method is being executed, hence for Method Local Inner Classes to access the method local variables, the compiler creates copies of these variables into the Heap, that's why these variables should be final or effectively final: so that the copies stored in the Heap are holding the actual valid values.

So I know Javac creates synthetic fields (fields that do not exist in the source code but are created by the compiler) inside the local inner classes to do so, and the question is how does that actually work, is it like creating a field inside the local inner class with the same as the method local variable which holds the same value as that variable so that inner class accesses that newly created one or is it something else, also what exactly does it mean for an element to be synthetic in general, apart from synthetic fields of method local inner classes? (I have read JLS , now hoping to find something more clear ?)

The synthetic field in the class file representing the local class has a mangled name.

javap -c -s can be used to find out what javac has done.

Take the following simple example class.

class Code {
    public static void main(String[] args) throws Throwable {
        class MyLocal {
            void fn() {
                System.err.println(args.length);
            }
        }
        new MyLocal().fn();
    }
}

Once compiled, javap -c -s Code\\$1MyLocal.class gives:

class Code$1MyLocal {
  final java.lang.String[] val$args;
    descriptor: [Ljava/lang/String;

  Code$1MyLocal();
    descriptor: ([Ljava/lang/String;)V
    Code:
       0: aload_0
       ...

The synthetic field for args is called val$args . I've added the -s option so we see that the no-args constructor for MyLocal actually has an argument when compiled. javap -c Code.class shows the bytecode for calling that constructor:

   0: new           #7                  // class Code$1MyLocal
   3: dup
   4: aload_0
   5: invokespecial #9                  // Method Code$1MyLocal."<init>":([Ljava/lang/String;)V

There is a slight difference between how the compiler does and how it would work if you did it yourself. If we look further down the disassembly for the local class constructor we see that it is assigning a field before calling the superclass constructor.

  Code$1MyLocal();
    descriptor: ([Ljava/lang/String;)V
    Code:
       0: aload_0
       1: aload_1
       2: putfield      #1                  // Field val$args:[Ljava/lang/String;
       5: aload_0
       6: invokespecial #7                  // Method java/lang/Object."<init>":()V
       9: return

That's the equivalent of:

    class MyLocal extends Object {
        private final String[] args;
        MyLocal(String[] args) {
            this.args = args;
            super();
        }
        ...
    }

That wont compile in Java. Before Java 1.4 this did not happen. You could end up observing the default value of the synthetic field, often causing a NullPointerException .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM