Simon Berens
Simon Berens

Reputation: 812

How are outer classes associated with inner classes in java?

I know each instance of an inner class in java is associated with an instance of its outer class, but I am wondering how this process goes about.

More specifically, when you write something like

public class Outer {
    Inner root;

    class Inner {
        public Inner() {
             next = (Math.random() > 0.5)? new Inner(): null;
        }

        Inner next;
    }
}


How are root, root.next, etc... all associated with the same instance of Outer? Does the compiler add a parameter to the Inner constructor?

Upvotes: 1

Views: 71

Answers (2)

Sotirios Delimanolis
Sotirios Delimanolis

Reputation: 279970

How are root, root.next, etc... all associated with the same instance of Outer?

The Java Language Specification section on Determining Enclosing Instances during class instance creation states:

Let C be the class being instantiated, and let i be the instance being created. If C is an inner class, then i may have an immediately enclosing instance (§8.1.3), determined as follows:

  • [...]
  • If C is an inner member class, then:
    • If the class instance creation expression is unqualified, then:
      • If the class instance creation expression occurs in a static context, then a compile-time error occurs.
      • Otherwise, if C is a member of a class enclosing the class in which the class instance creation expression appears, then let O be the immediately enclosing class of which C is a member. Let n be an integer such that O is the n'th lexically enclosing type declaration of the class in which the class instance creation expression appears.

        The immediately enclosing instance of i is the n'th lexically enclosing instance of this.

      • Otherwise, a compile-time error occurs.

In other words, when you perform

next = (Math.random() > 0.5)? new Inner(): null;

Inner is the class being instantiated; Inner is a member of a class enclosing the class (Inner, ie. itself) where the new Inner() appears; Outer is the immediately enclosing class of Inner; because Inner is an inner class of Outer, there has to be a this and that this is guaranteed to have an Outer enclosing instance; and, finally, that enclosing instance becomes the enclosing instance of i, the new Inner instance being created.

In short, it reuses the same instance of Outer.

Does the compiler add a parameter to the Inner constructor?

The Java Language Specification section on the Formal Parameters of Constructors states:

The constructor of a non-private inner member class implicitly declares, as the first formal parameter, a variable representing the immediately enclosing instance of the class (§15.9.2, §15.9.3).

So, yes.

Upvotes: 2

m0skit0
m0skit0

Reputation: 25873

First note that Java does not have any support for inner classes so actually Inner class is compiled as a separate class. This is denoted as Outer$Inner. Here's the bytecode:

// class version 52.0 (52)
// access flags 0x20
class Outer$Inner {

  // compiled from: Outer.java
  // access flags 0x0
  INNERCLASS Outer$Inner Outer Inner

  // access flags 0x0
  LOuter$Inner; next

  // access flags 0x1010
  final synthetic LOuter; this$0

  // access flags 0x1
  public <init>(LOuter;)V
   L0
    LINENUMBER 5 L0
    ALOAD 0
    ALOAD 1
    PUTFIELD Outer$Inner.this$0 : LOuter;
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
   L1
    LINENUMBER 6 L1
    ALOAD 0
    INVOKESTATIC java/lang/Math.random ()D
    LDC 0.5
    DCMPL
    IFLE L2
    NEW Outer$Inner
    DUP
    ALOAD 1
    INVOKESPECIAL Outer$Inner.<init> (LOuter;)V
    GOTO L3
   L2
   FRAME FULL [Outer$Inner Outer] [Outer$Inner]
    ACONST_NULL
   L3
   FRAME FULL [Outer$Inner Outer] [Outer$Inner Outer$Inner]
    PUTFIELD Outer$Inner.next : LOuter$Inner;
   L4
    LINENUMBER 7 L4
    RETURN
   L5
    LOCALVARIABLE this LOuter$Inner; L0 L5 0
    LOCALVARIABLE this$0 LOuter; L0 L5 1
    MAXSTACK = 5
    MAXLOCALS = 2
}

As you correctly guessed, the Outer class instance is passed to the constructor of the Inner class here public <init>(LOuter;)V

Upvotes: 0

Related Questions