让我们看一下程序:
public class Outer
{
public Outer() {}
class Inner1 extends Outer
{
public Inner1()
{
super(); // invokes Object() constructor
}
}
class Inner2 extends Inner1
{
public Inner2()
{
super(); // invokes Inner1() constructor
}
}
}
如果尝试编译它,就会发生错误。
Outer.java:12: cannot reference this before
supertype constructor has been called
super(); // invokes Inner1() constructor
因为超类Inner2
本身就是一个内部类,所以一个晦涩的语言规则开始发挥作用。如您所知,内部类的实例化,例如
Inner1
,需要将封闭实例提供给构造函数。通常,它是隐式提供的,但也可以通过超类构造函数调用形式显式提供expression.super(args)
如果封闭实例是隐式提供的,编译器会生成表达式: 它使用 this 引用作为超类是其成员的最内层封闭类。诚然,这有点拗口,但这是编译器所做的。在这种情况下,超类是Inner1
。因为当前类 ,
Inner2
间接扩展 Outer,所以它具有 Inner1 作为继承成员。因此,超类构造函数的限定表达式就是 this。编译器提供一个封闭实例,将 super 重写为 this.super。
默认Inner2
构造函数在调用超类构造函数之前尝试引用
this
,这是非法的。
解决此问题的蛮力方法是显式提供合理的封闭实例:
public class Outer
{
class Inner1 extends Outer { }
class Inner2 extends Inner1
{
public Inner2()
{
Outer.this.super();
}
}
}
这可以编译,但很复杂。有一个更好的解决方案:
每当你写一个成员类时,问问自己,这个类真的需要一个封闭的实例吗?如果答案是否定的,请将其设为 static。内部类有时很有用,但它们很容易引入使程序难以理解的复杂性。它们与泛型、反射和继承有复杂的交互。如果你声明Inner1
是静态的,问题就会消失。如果你也声明Inner2
为静态的,你就可以真正理解程序做了什么。
总之,一个类既是内部类又是另一个类的子类很少适合。更一般地说,扩展内部类很少合适。如果必须,请仔细考虑封闭的实例。大多数成员类可以并且应该声明为静态的。