Consider class OuterClass
which has InnerClass
public class OuterClass {
class InnerClass {
}
}
And the second class, which is trying to extend InnerClass
of OuterClass
public class Clazz extends OuterClass.InnerClass {
public Clazz(OuterClass outerClass) {
outerClass.super();
}
}
So far so good, this code will work, compiler should not give warnings.
But I am trying to understand - why is that necessary to pass to constructor reference of OuterClass? and why is that necessary to call it's super constructor?
I want to understand why is it has to be exact this way?
Also, consider this class (not related to previous ones) in which classes Apartments
and Hall
are inner classes to Building
class which is inner to Solution
(innerception).
public class Solution {
public class Building {
public class Hall {
private BigDecimal square;
public Hall(BigDecimal square) {
this.square = square;
}
}
public class Apartments {
}
}
}
Then there goes top-level class which is trying to extend inner inner class Hall
class BigHall extends Solution.Building.Hall {
public BigHall(Solution.Building building, BigDecimal square)
{
building.super(square);
}
}
Look at this mess. Last two classes should compile as well, but can you clear it up for me, why BigHall
class when extending inner inner class Hall
does need to pass to constructor reference to Building object instead of Solution object?
If you can provide quotation from JLS that'd be great!
A nested class is a member of its enclosing class. Non-static nested classes (inner classes) have access to other members of the enclosing class, even if they are declared private. Static nested classes do not have access to other members of the enclosing class.
Inner classes are a security mechanism in Java. We know a class cannot be associated with the access modifier private, but if we have the class as a member of other class, then the inner class can be made private. And this is also used to access the private members of a class.
An inner class is a class that is nested or defined within another class. On the other hand, a subclass is a class that is derived from another class.
An instance of InnerClass can exist only within an instance of OuterClass and has direct access to the methods and fields of its enclosing instance.
Your InnerClass
is an inner class.
An inner class is a nested class that is not explicitly or implicitly declared
static
.
Inner classes may have enclosing instances
An instance
i
of a direct inner classC
[yourInnerClass
] of a class or interfaceO
[yourOuterClass
] is associated with an instance ofO
, known as the immediately enclosing instance ofi
.
Only inner classes declared in static contexts don't have enclosing instances.
An instance of an inner class
I
whose declaration occurs in a static context has no lexically enclosing instances.
Your example's inner class is not in a static context and therefore requires an enclosing instance.
The Java Language Specification then 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
(It goes into further detail as to why, if you're interested). In other words, your InnerClass
constructor really looks like
public InnerClass(OuterClass OuterClass.this){} // this is valid syntax for entirely different reasons
It expects an argument of type OuterClass
to use as its enclosing instance. Subclassing this InnerClass
type does not change that, especially since any subtype must invoke its supertype's super constructor.
On the subject of constructors, the specification also states
If a constructor body does not begin with an explicit constructor invocation and the constructor being declared is not part of the primordial class
Object
, then the constructor body implicitly begins with a superclass constructor invocation "super();", an invocation of the constructor of its direct superclass that takes no arguments.
So obviously, without the parameter, your code wouldn't work
public class Clazz extends OuterClass.InnerClass {
public Clazz() {
// implicit super() invocation
}
}
This super()
constructor invocation wouldn't work. In this case, it's because it's the wrong syntax. But the idea is that the super constructor is expecting an argument for the formal parameter representing the enclosing instance, but you aren't providing one. The correct syntax is the one you already have. Let's define it.
There are multiple kinds of constructor invocations. Yours
public Clazz(OuterClass outerClass) {
outerClass.super();
}
is a qualified superclass constructor invocation, defined as
Qualified superclass constructor invocations begin with a
Primary
expression or anExpressionName
. They allow a subclass constructor to explicitly specify the newly created object's immediately enclosing instance with respect to the direct superclass (§8.1.3). This may be necessary when the superclass is an inner class.
The chapter goes one to explain how the expression is evaluated
If the superclass constructor invocation is qualified, then the
Primary
expression or theExpressionName
immediately preceding ".super
",p
, is evaluated.
[...]Otherwise, the result of this evaluation is the immediately enclosing instance of
i
with respect toS
.
In other words, the object referenced by outerClass
becomes the required enclosing instance of your Clazz
instance.
In simplified terms, ignoring the case of inner classes, your example boils down to something like
public class Foo {}
public class Bar {
public Bar(Foo foo){}
}
public class SubBar extends Bar {
public SubBar(Foo foo) {
super(foo);
}
}
SubBar
has to satisfy the Bar
super constructor that expects a Foo
instance. This is the same thing that's happening with your Clazz
type except its super type's constructor is implicit.
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