为什么内部类的子类需要一个封闭实例



考虑具有InnerClass的类OuterClass

public class OuterClass {
class InnerClass {
}
}

第二类,尝试扩展OuterClassInnerClass

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

到目前为止还不错,这段代码会正常工作,编译器不应该给出警告。

但我试图理解——为什么有必要将其传递给OuterClass的构造函数引用?为什么有必要调用它的超级构造函数?

我想知道为什么必须如此精确?

此外,考虑这个类(与以前的类无关),其中类ApartmentsHallBuilding的内部类,CCD_7是Solution的内部类(内部感受)。

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

然后是顶级类,它试图扩展内部内部类Hall

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

看看这一团糟。最后两个类也应该编译,但你能帮我澄清一下吗,为什么BigHall类在扩展内部内部类Hall时需要传递给Building对象的构造函数引用,而不是Solution对象?

如果你能提供JLS的报价,那就太好了!

您的InnerClass内部类

内部类是一个非显式或隐式的嵌套类声明为CCD_ 12。

内部类可能具有封闭实例

直接内部类C[您的InnerClass]的实例i类或接口O[您的OuterClass]O的实例,称为i立即封闭实例。

只有在静态上下文中声明的内部类才没有封闭实例。

内部类I的实例,其声明发生在静态上下文没有词汇上封闭的实例。

示例的内部类不在静态上下文中,因此需要一个封闭实例

Java语言规范随后规定

非私有内部成员类的构造函数隐式声明一个变量作为第一个形式参数,该变量表示立即封闭类的实例

(如果您感兴趣,它会进一步详细说明原因)。换句话说,你的InnerClass构造函数看起来真的很像

public InnerClass(OuterClass OuterClass.this){} // this is valid syntax for entirely different reasons

它期望OuterClass类型的参数用作其封闭实例。子类化这个InnerClass类型不会改变这一点,特别是因为任何子类型都必须调用其超类型的超级构造函数。

关于构造函数,规范还规定了

如果构造函数主体不是以显式构造函数开头调用和正在声明的构造函数不是原始类Object则构造函数主体隐式开始通过超类构造函数调用"super();">不接受参数的直接超类的构造函数。

很明显,如果没有这个参数,你的代码将无法在中工作

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

super()构造函数调用不起作用。在这种情况下,这是因为它的语法错误。但其想法是,超级构造函数期望为表示封闭实例的形式参数提供一个参数,但您没有提供。正确的语法是您已经拥有的语法。让我们定义一下。

构造函数调用有多种类型。你的

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

合格的超类构造函数调用,定义为

合格的超类构造函数调用Primary开始表达或CCD_ 27它们允许子类构造函数显式指定新创建的对象的立即封闭关于直接超类的实例(§8.1.3)。这可能是当超类是内部类时是必需的

本章首先解释了如何评估表达式

如果超类构造函数调用是合格的,则CCD_ 28表达或紧接在前的CCD_评估".super",即p
[…]

否则,此评估的结果是立即封闭32相对于CCD_ 33的实例

换句话说,outerClass引用的对象将成为Clazz实例所需的封闭实例


简单地说,忽略内部类的情况,您的示例可以归结为类似的内容

public class Foo {}
public class Bar {
public Bar(Foo foo){}
}
public class SubBar extends Bar {
public SubBar(Foo foo) {
super(foo);
}
}

SubBar必须满足期望Foo实例的Bar超级构造函数。这与Clazz类型发生的情况相同,只是它的超类型的构造函数是隐式的。

为什么需要将其传递到OuterClass的构造函数引用

仅仅因为InnerClass的实例只能存在于OuterClass的实例中,并且可以直接访问其封闭实例的方法和字段。这就是Java中内部(非静态)类的设计方式。

UPD当您请求JLS参考时,亲爱的@Boris the Spider在评论中提供了两次,它是JLS 8.1.3。但我个人觉得JLS对这件事的描述相当混乱。官方的JavaDoc教程以更简单的方式解释了这些事情。

考虑以下

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
}
}
}

在这个例子中。。。每个CCD_ 42需要一个CCD_。Inner可以访问Outer中的所有字段

但CCD_ 46并不需要CCD_。它无法从Outer访问字段

内部类可以由外部类之外的另一个类扩展。如果您正在扩展静态内部类(静态嵌套类),那么它是一个直接的实现。如果要扩展非静态内部类,则子类构造函数必须使用外部类的实例显式调用超类构造函数。因为,如果没有外部类的实例,就无法访问非静态内部类。

样本代码

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
}
}

编译器生成的内部代码

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

相关内容

最新更新