Caveat #1: This is actually a potential two-parter: First, does the constructor for a private inner class have a formal parameter? If yes, why does the JLS say it doesn't? And if no, how/why not?
Caveat #2: This question is not for speculation. I'm looking for authoritative answers only.
Default constructors are defined in JLS 8.8.9, which states (in part):
The default constructor has no formal parameters, except in a non-private inner member class, where the default constructor implicitly declares one formal parameter representing the immediately enclosing instance of the class (§8.8.1, §15.9.2, §15.9.3).
(emphasis added)
The "non-private" bit seems odd to me: in order for an inner class to access fields defined in its enclosing class, it needs a reference to that instance. This should be the same regardless of whether the inner class is private.
In fact, javac seems to agree with me, in contradiction to the spec. If I compile this:
public class Ctors {
private class MyInner {
}
}
...and run javap -c -private
, then we see a constructor with a single formal parameter, for the instance of the enclosing class:
$ javap -c -private Ctors\$MyInner
Compiled from "Ctors.java"
class Ctors$MyInner {
final Ctors this$0;
private Ctors$MyInner(Ctors);
Code:
0: aload_0
1: aload_1
2: putfield #1 // Field this$0:LCtors;
5: aload_0
6: invokespecial #2 // Method java/lang/Object."<init>":()V
9: return
}
For reference, this is on Oracle JDK 1.8.0_05.
So the JLS says that the default constructor for private inner member classes has no formal parameters, but javac/javap say it has one. (My understanding of the most natural way for things to work would also say it should have one, for the little that's worth.) Which is right, and why does the JLS specifically exclude private inner classes?
There is a difference between the implementation and the specification.
In my opinion the "except" JLS statement
...except in a non-private inner member class...
is poorly worded.
It means, the compiler is not required to implicitly declares one formal parameter representing the immediately enclosing instance of the class... but it could.
Why implicitly formal parameter is required in non-private inner member class ?
From JLS 8.8.1:
The member class may have been emitted by a compiler which is different than the compiler of the class instance creation expression. Therefore, there must be a standard way for the compiler of the creation expression to pass a reference (representing the immediately enclosing instance) to the member class's constructor
For example if i compile this inner class with a first compiler:
package p1;
public class Ctors {
public class MyInner {
}
}
if i want to compile this sub class with another compiler:
package p2;
import p1.Ctors;
public class SubCtors {
public SubCtors() {
new Ctors();
}
}
the second compiler must be able to use the default constructor with the formal parameter.
In this case the instance of the enclosing class with a SubCtors
instance.
Why implicitly formal parameter is not required in non-private inner member class ?
Because a non-private inner member class is always accessed by the same compiler that compiled it. As you shown, javac generates the same constructor regardless to the class visibility but it is not require to. Another compiler implementation is free to choose another way.
There is also another point in JLS 8.8.1 which is very much along the same line
In a class instance creation expression for a local class (not in a static context) or anonymous class, §15.9.2 specifies the immediately enclosing instance of the local/anonymous class. The local/anonymous class is necessarily emitted by the same compiler as the class instance creation expression. That compiler can represent the immediately enclosing instance how ever it wishes. There is no need for the Java programming language to implicitly declare a parameter in the local/anonymous class's constructor.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With