Reputation: 812
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
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 leti
be the instance being created. IfC
is an inner class, theni
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 letO
be the immediately enclosing class of whichC
is a member. Letn
be an integer such thatO
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 ofthis
.- 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
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