The Java Qualified Superclass Constructor Invocation

In Java, a nested class is a class declared within the body of another class. Nested classes can be either static or non-static. The non-static kind is commonly referred to as inner classes and maintain a special relationship with their enclosing class.
When a class extends an inner class, a specific syntax may be required in the constructor to provide the superclass's enclosing instance. This is what we'll discuss in this blog post.
Our Running Example
Let's start with the following class hierarchy:
public class Outer { public class Inner {}}
Here, we've defined a top-level class named Outer
, which contains an inner class named Inner
.
Next, let's declare a new class named Example
:
public class Example extends Outer.Inner { // Warning: does not compile!}
It is also a top-level class and attempts to extend the Inner
class defined earlier.
However, when we try to compile these classes, the compilation fails with the following error:
Example.java:1: error:
an enclosing instance that contains Outer.Inner is required
public class Example extends Outer.Inner {
^
1 error
The error message indicates that an enclosing instance of Outer
is required.
But how can we provide one?
Inner Classes
Before addressing the issue with our Example
class,
let's take a closer look at the error message.
The same error occurs if we try to create an instance of Inner
as though it were a static nested class:
// Warning: does not compile!Outer.Inner inner = new Outer.Inner();
When we compile this code along with the Outer
class, the compilation fails with the following error:
Test.java:3: error:
an enclosing instance that contains Outer.Inner is required
Outer.Inner inner = new Outer.Inner();
^
1 error
Notice that the error message is identical to what we encountered earlier.
Once again, it explicitly states that an enclosing instance of Outer
is required.
To resolve this, we update the main
method:
Outer.Inner inner = new Outer().new Inner();
In the updated code:
-
We create an instance of the enclosing class,
Outer
. -
Using this instance, we create an instance of the inner class,
Inner
.
Alternatively, this can be expressed in two separate statements:
Outer outer = new Outer();Outer.Inner inner = outer.new Inner();
Both approaches successfully compile. The key takeaway is that to create an instance of an inner class, you must first instantiate its enclosing class.
The Qualified Superclass Constructor Invocation
Let's return to our Example
class.
It is a subclass of Inner
, an inner class.
This means that an instance of Example
is implicitly also an instance of Inner
.
Consequently, to create an instance of Example
, we must first provide an instance of Outer
.
Using a Primary Expression
One solution is to use a qualified superclass constructor invocation with a primary expression:
public class Example extends Outer.Inner { Example() { new Outer().super(); }}
In the Example
constructor:
-
We create an instance of
Outer
, the enclosing class of the superclass. -
Using this instance, we invoke the
Example
superclass constructor, which corresponds to theInner
constructor, via thesuper()
call.
Let's see this in action.
First, we update the Outer
and Inner
classes as follows:
public class Outer { Outer() { System.out.println("Outer()"); } public class Inner { Inner() { System.out.println("Inner()"); } }}
To observe the constructor invocation,
we've added explicit constructors that print a message when invoked.
Now, let's write a program to create an instance of the Example
class:
void main() { new Example(); }
When executed, the program prints:
Outer()
Inner()
In the Example
constructor,
we use a qualified superclass constructor invocation with a primary expression, new Outer()
,
to create and provide the required enclosing instance.
Using an Expression Name
Alternatively, we can pass an explicit Outer
instance to the Example
constructor:
public class Example extends Outer.Inner { Example(Outer outer) { outer.super(); }}
In this approach, we update the main
method as follows:
void main() { Outer outer = new Outer(); new Example(outer);}
This version of the program produces the same output:
Outer()
Inner()
By passing an explicit Outer
instance,
the super()
call uses the provided instance to invoke the Inner
constructor.
Both approaches demonstrate how a qualified superclass constructor invocation
provides the enclosing instance for the superclass of our Example
class.
It's All in the JLS
What we've shown in the examples is explicitly defined in the Java Language Specification (JLS). In particular, Section 8.8.7.1, titled "Explicit Constructor Invocations," provides the following production:
ExplicitConstructorInvocation:
[TypeArguments] this ( [ArgumentList] ) ;
[TypeArguments] super ( [ArgumentList] ) ;
ExpressionName . [TypeArguments] super ( [ArgumentList] ) ;
Primary . [TypeArguments] super ( [ArgumentList] ) ;
Pay particular attention to the last two lines.
They define how super()
can be invoked either from an expression name or a primary expression.
Not Restricted to Top-Level Classes
An inner class must also use the qualified superclass constructor invocation in its constructor if it extends another inner class with a different enclosing type. To illustrate, let's first examine a scenario where the qualified superclass constructor invocation is not required:
public class Outer { public class Inner {} public class NotRequired extends Inner {}}
This compiles without errors.
Here, the NotRequired
class is:
-
An inner class with respect to
Outer
. -
A direct subclass of
Inner
.
Since both NotRequired
and Inner
share the same enclosing class (Outer
),
their enclosing instances have the same type,
and no special constructor invocation is needed.
Now, consider the following class hierarchy:
public class Outer { public class Inner {} public static class NewContext { public class Required extends Inner { Required() { new Outer().super(); } } }}
In this example, although Required
and Inner
are both enclosed in Outer
,
their immediate enclosing classes differ. Specifically:
-
Inner
is directly enclosed byOuter
. -
Required
is directly enclosed byNewContext
.
Since the enclosing instances of Required
and Inner
have different types,
creating an instance of Required
requires explicitly providing an instance of Outer
,
the enclosing class of its direct superclass.
Final Note
In the first example of the previous section, while the qualified superclass constructor invocation is not required, it is still allowed. This means the following code compiles without errors:
public class Outer { public class Inner {} public class NotRequired extends Inner { NotRequired() { new Outer().super(); } }}
However, this version is semantically different from the original.
In this case, the enclosing instance of NotRequired
is a distinct, newly created Outer
object.
Conclusion
In this blog post, we discussed the qualified superclass constructor invocation. This uncommon language construct is required when the superclass is an inner class; before invoking the superclass constructor, we must provide the enclosing instance of the superclass.
I haven't found uses of the qualified superclass constructor invocation in the wild. Granted, I've never looked for it specifically, but I don't remember seeing it in any of the code I've read.
I found this topic interesting enough to share, as it highlights a less commonly discussed corner of the language. However, unless you encounter a specific use case that demands this approach, I'd recommend avoiding it purely for novelty's sake. Simplicity and readability should remain priorities in your code.