为什么内部 class 的子 class 需要封闭实例?

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

考虑 class OuterClass 其中有 InnerClass

public class OuterClass {
    class InnerClass {

    }
}

和第二个 class,它试图扩展 OuterClass

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

到目前为止一切顺利,这段代码可以工作,编译器应该不会给出警告。

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

我想了解为什么必须这样精确?

此外,考虑这个 class(与之前的无关),其中 classes ApartmentsHall 是 classes 到 Building class 是 Solution 的内在(innerception)。

public class Solution {
    public class Building {
        public class Hall {
            private BigDecimal square;

            public Hall(BigDecimal square) {
                this.square = square;
            }
        }

        public class Apartments {
        }
    }
}

然后是顶层 class 试图扩展 inner inner class Hall

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

看看这个烂摊子。最后两个 classes 应该也可以编译,但是你能帮我弄清楚吗,为什么 BigHall class 扩展 inner inner class Hall 确实需要将构造函数引用传递给 Building 对象而不是 Solution 对象?

如果能提供JLS的报价就好了!

考虑以下因素

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

在此示例中...每个 Inner 都需要一个 Outer 才能存在。 Inner 可以访问 Outer

中的所有字段

但是 StaticInner 不需要 Outer 就可以存在。它无法访问来自 Outer

的字段

为什么需要传递给 OuterClass 的构造函数引用?

仅仅是因为 InnerClass 的实例只能存在于 OuterClass 的实例中,并且可以直接访问其封闭实例的方法和字段。内(non-static)类are designed in Java就是这样。

UPD 当你要求 JLS 参考时,亲爱的 @Boris the Spider 在评论中提供了两次,它是 JLS 8.1.3。但是我个人觉得 JLS 对这个东西的描述相当混乱。官方 Java 文档教程以更简单的方式对这些内容进行了解释。

一个内部 class 可以在它的外部 class 之外被另一个 class 扩展。如果您要扩展静态内部 class(静态嵌套 class),那么它是一个直接的实现。如果要扩展 non-static inner class,则 sub class 构造函数必须使用 outer class 的实例显式调用 super class 构造函数。因为,如果没有 outer class.

的实例,您将无法访问 non-static inner class

示例代码

    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[=11=];  
    Outer$Inner()  
    {   super();  
        this[=11=] = Outer.this;  
    }
}

您的 InnerClassinner class

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

内部 classes 可能有封闭实例

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

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

An instance of an inner class I whose declaration occurs in a static context has no lexically enclosing instances.

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

Java Language Specification 然后说

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

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

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

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

关于构造函数,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.

很明显,如果没有参数,您的代码将无法运行

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

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

multiple kinds of constructor invocations个。你的

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

合格的超级class构造函数调用defined

Qualified superclass constructor invocations begin with a Primary expression or an ExpressionName. 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.

chapter 解释了如何计算表达式

If the superclass constructor invocation is qualified, then the Primary expression or the ExpressionName immediately preceding ".super", p, is evaluated.
[...]

Otherwise, the result of this evaluation is the immediately enclosing instance of i with respect to S.

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


简而言之,忽略内部 classes 的情况,您的示例可以归结为

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 类型发生的事情相同,只是它的超类型的构造函数是隐式的。