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

时间:2017-02-07 15:46:05

标签: java inheritance inner-classes

考虑具有OuterClass

的班级InnerClass
public class OuterClass {
    class InnerClass {

    }
}

第二节课,试图扩展InnerClass

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

到目前为止这么好,这段代码会起作用,编译器不应该发出警告。

但我想了解 - 为什么需要传递给 OuterClass 的构造函数引用?为什么有必要称它为超级构造函数?

我想明白为什么必须这样确切?

另外,考虑这个类(与以前的类无关),其中类ApartmentsHallBuilding类的内部类,它是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时确实需要传递给构建对象而不是解决方案对象? / p>

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

4 个答案:

答案 0 :(得分:7)

您的InnerClassinner class

  

内部类是一个非显式或隐式的嵌套类   声明static

内部类可能包含实例

  

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

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

  

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

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

Java Language Specification然后陈述

  

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

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

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

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

关于构造函数的主题,specification also states

  

如果构造函数体不以显式构造函数开头   调用和声明的构造函数不是   原始类Object然后构造函数体隐式开始   使用超类构造函数调用“super();”,调用   它的直接超类的构造函数,不带参数。

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

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

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

multiple kinds of constructor invocations。此致

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

限定的超类构造函数调用defined

  

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

chapter用来解释如何评估表达式

  

如果超类构造函数调用是合格的,那么   Primary表达式或前一个ExpressionName   评估“.super”,p   [...]

     

否则,此评估的结果是立即封闭   关于i S的实例。

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

简单来说,忽略内部类的情况,你的例子归结为类似

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

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

答案 1 :(得分:3)

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

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

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

答案 2 :(得分:0)

考虑以下

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

中的字段

答案 3 :(得分:0)

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

示例代码

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