Yihang Ho bio photo

Yihang Ho

Coder

Twitter Github Email

Java supports nested classes - classes that exist in the body of other classes. As we know, when a piece of Java code is compiled, one or more Java class files are produced. Each class file fully represents a single class or interface. So what happens if we chuck a class inside another class? Do we have the analogous nested class files or something like that? Turns out nested classes are merely syntactical sugar provided by the compiler. The JVM knows absolutely nothing about the fact that certain classes are nested.

What the Java compiler does is that it moves nested classes to become regular classes and generates or modifies the constructors of these nested classes to take in a reference of an instance of the outer class. This reference is kept to in order to access non-private fields.

If we compile this piece of code, two class files will be generated - Outer.class and Outer$Nested.class.

// Outer.java
class Outer {
  int x = 10;

  class Nested {
    int add(int y) {
      return x + y;
    }
  }
}

It is obvious that the Nested class now becomes the Outer$Nested class. The add method of Outer$Nested needs to access x of the Outer class. The Java disassembler, javap, can be used to show the signature of a class. Doing that on Outer$Nested can give us an idea how Outer$Nested accesses x:

$ javac Outer.java      # Generate Outer.class and Outer$Nested.class
$ javap Outer\$Nested   # Note that we have to escape the dollar sign
Compiled from "Outer.java"
class Outer$Nested {
  final Outer this$0;
  Outer$Nested(Outer);
  int add(int);
}

As predicted, the constructor takes a reference of an instance of the Outer class. If we look at the bytecode, we can see that this reference is kept in the this$0 field.

Hence, we can imagine that the decompiled code looks something like this:

class Outer {
  int x = 10;
}

class Outer$Nested {
  Outer this$0;

  Outer$Nested(Outer t) {
    this$0 = t;
  }

  int add(int y) {
    return this$0.x + y;
  }
}

However, this approach will not work if x is private as this$0.x will be illegal. What the compiler does this time is that it generates an accessor method to expose the value of x.

// Outer.java
class Outer {
  private int x = 10; // Notice that x is now private

  class Nested {
    int add(int y) {
      return x + y;
    }
  }
}

Again, we can disassemble the generated class files to see the class signature:

$ javac Outer.java    # Generates Outer.class and Outer$Nested.class
$ javap Outer\$Nested # The output is similar to the previous run
$ javap -p Outer      # The -p flag outputs private members as well
Compiled from "Outer.java"
class Outer {
  private int x;
  Outer();
  static int access$000(Outer);
}

We can see that the compiler has synthesized the access$000 method. It can be verified, again by looking at the bytecode, that this method indeed returns the value of x and is used by the add method of Outer$Nested. We can imagine that the transformation looks like this:

class Outer {
  private int x = 10;

  static int access$000(Outer t) {
    return t.x;
  }
}

class Outer$Nested {
  Outer this$0;

  Outer$Nester(Outer t) {
    this$0 = t;
  }

  int add(int y) {
    return Outer.access$000(this$0) + y;
  }
}

The examples presented here are using inner class, but the idea is similar for other types of nested classes, which are static nested class, local class, and anonymous class. The constructors for local classes and anonymous classes takes in additional variables in the scope that they are declared. Also, somehow, javap does not give the correct signature for the constructors of local classes, but the bytecode shows that the mechanism is as described here.

Despite the fact that fields and methods like this$0 and access$000 are generated, they can never be used in your Java code. This is because the $ symbol cannot be used as part of a valid identifier.

In short, the concept of nested classes is a concept and syntactical sugar introduced by the Java compiler. This is achieve by generating or modifying the constructors of the nested class to take in a reference of an instance of outer class to access its non-private members, and by generating accessor methods to access the private members of the outer class.