プログラムを見てみましょう:
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があります。したがって、スーパークラスコンストラクターの修飾式は単純にこれです。コンパイラはそれを囲むインスタンスを提供し、superをthis.superに書き換えます。
デフォルトのコンストラクターは、スーパークラスコンストラクターが呼び出される前Inner2
に参照を試み
ますが、これは不正です。this
この問題を修正する力ずくの方法は、合理的な囲みインスタンスを明示的に提供することです。
public class Outer
{
class Inner1 extends Outer { }
class Inner2 extends Inner1
{
public Inner2()
{
Outer.this.super();
}
}
}
これはコンパイルされますが、複雑です。より良い解決策があります:
メンバークラスを書くときはいつでも、このクラスは本当にそれを囲むインスタンスを必要としますか?答えが「いいえ」の場合は、静的にします。内部クラスは便利な場合もありますが、プログラムを理解しにくくする複雑さを簡単にもたらす可能性があります。それらは、ジェネリックス、リフレクション、および継承と複雑な相互作用を持っています。Inner1
静的であると宣言すると、問題は解決します。静的であると宣言する場合もInner2
、プログラムの機能を実際に理解できます。
要約すると、あるクラスが別のクラスの内部クラスとサブクラスの両方になることはめったに適切ではありません。より一般的には、内部クラスを拡張することが適切になることはめったにありません。必要に応じて、囲んでいるインスタンスについてじっくり考えてください。ほとんどのメンバークラスは、静的として宣言でき、宣言する必要があります。