Why does a subclass of an inner class require an enclosing instance?

Consider the OuterClass class that has an InnerClass

 public class OuterClass { class InnerClass { } } 

And the second class that is trying to extend InnerClass from OuterClass

 public class Clazz extends OuterClass.InnerClass { public Clazz(OuterClass outerClass) { outerClass.super(); } } 

So far, so good, this code will work, the compiler should not give warnings.

But I'm trying to understand - why should I go to the link to the OuterClass constructor? and why should it be called a super constructor?

I want to understand why it should be exactly that way?

We also consider this class (not related to the previous ones), in which the Apartments and Hall classes are internal classes of the Building class, which is internal to Solution (interception).

 public class Solution { public class Building { public class Hall { private BigDecimal square; public Hall(BigDecimal square) { this.square = square; } } public class Apartments { } } } 

Then comes the top-level class, which is trying to extend the inner inner class of Hall

 class BigHall extends Solution.Building.Hall { public BigHall(Solution.Building building, BigDecimal square) { building.super(square); } } 

Look at this mess. The last two classes should also compile, but you can clear it for me, why should the BigHall class when BigHall inner inner class of the Hall have to pass a constructor reference to the Building object instead of the Solution object?

If you can provide a quote from JLS, that would be great!

+6
source share
4 answers

Your InnerClass is an inner class .

An inner class is a nested class that is explicitly or implicitly declared static .

Inner classes may contain instances

Example i direct inner class C [your InnerClass ] class or interface O [your OuterClass ] is associated with an instance of O , known as a direct instance of i .

Only inner classes declared in static contexts do not contain instances.

An instance of inner class i whose declaration occurs in a static context does not have lexically-closing instances.

Your inner example class is not in a static context, so it requires an enclosing instance.

The Java language specification then indicates

The constructor of a nonunit inner member class implicitly declares a variable representing the immediately including instance of the class as the first formal parameter

(More on 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 instance. Subclassing this type of InnerClass does not change this, moreover, any subtype must refer to a super-super super constructor.

For constructors, the specification also indicates

If the constructor body does not start with an explicit constructor call and the declared constructor is not part of the primordial class Object , then the constructor body implicitly begins with a call to the constructor of the superclass "super ();" , calling the constructor of its direct superclass that takes no arguments.

So, without a parameter, your code will not work

 public class Clazz extends OuterClass.InnerClass { public Clazz() { // implicit super() invocation } } 

This super() constructor call does not work. In this case, this is because it is the wrong syntax. But the idea is that the superconstructor expects an argument for a formal parameter representing the spanning instance, but you do not provide it. The correct syntax is the one you already have. Let him determine it.

There are several types of constructor calls . Your

 public Clazz(OuterClass outerClass) { outerClass.super(); } 

is a qualified call to the superclass constructor, defined as

Qualified superclass constructor calls start with a Primary expression or ExpressionName . They allow the constructor of the subclass to explicitly specify a newly created object that immediately includes an instance relative to the direct superclass (ยง8.1.3). This may be necessary when the superclass is an inner class.

The chapter explains how the expression is evaluated.

If the call to the superclass constructor is qualified, then Primary or ExpressionName immediately preceding " .super ", p . [...]

Otherwise, the result of this evaluation will immediately include instance i relative to S

In other words, the object referenced by OuterClass becomes the required instance of your Clazz instance.


In simplified expressions, ignoring the case of inner classes, your example comes 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 must satisfy the super constructor Bar , which expects an instance of Foo . This is the same as your Clazz type, with the exception of its supertype constructor, implicit.

+3
source

Why is it necessary to pass a reference to the OuterClass constructor?

Just because an InnerClass instance can exist only inside an OuterClass instance and has direct access to the methods and fields of its containing instance. This is how internal (non-static) classes are embedded in Java .

UPD When you asked for a link to JLS, Dear @Boris Spider provides it twice in the comments, this is JLS 8.1. 3 . But I personally find the JLS description of this thing rather confusing. The official Java Doc tutorials explain these things in a simpler way.

+3
source

Consider the following

 public class Outer { String outerString; class Inner { public void doStuff() { System.out.println("Outer string is " + outerString); } } static class StaticInner { public void doStuff() { // can't access outerString here } } } 

In this example ... each Inner requires the existence of an Outer . Inner can access all fields from Outer

But StaticInner does not need the existence of Outer . He cannot access fields from Outer

0
source

An inner class can be extended by another class outside its outer class. If you extend a static inner class (static nested class), then this is a simple implementation. If you are extending a non-static inner class, then the constructor of the helper class must explicitly call the superclass of the class using an instance of the outer class. Because you cannot get a non-static inner class without an instance of the outer class.

Code example

  class OuterClass { class InnerClassTwo { //Class as a non-static member } } //Extending non-static inner class or member inner class class AnotherClassTwo extends OuterClass.InnerClassTwo { public AnotherClassTwo() { new OuterClass().super(); //accessing super class constructor through OuterClass instance } } 

Compiler generated internal code

 class Outer$Inner { final Outer this$0; Outer$Inner() { super(); this$0 = Outer.this; } } 
0
source

Source: https://habr.com/ru/post/1014769/


All Articles