3

私は内部クラスの次のケースを持っています。

class Outer {
   private class Inner1 extends InnerBase {
   }
   private class Inner2 extends InnerBase {
   }
   private class InnerBase {
   }
}

通常、私は内部クラスが追加の隠し「これ」を外部クラスに持つと考えていました。

しかし、内部クラスが別の内部クラスから派生した場合はどうなるでしょうか。

すべての内部クラス (Inner1、Inner2、InnerBase) には、追加の this が必要です。

Inner1、Inner2 は、outer this への独自の参照を持ちますか? または、InnerBase のものを再利用するだけで、動作が少し異なりますか?

(これを非表示 == 外部クラスのインスタンスを指す参照)

4

4 に答える 4

2

内部的には、階層内の各非静的内部クラスは、への独自の参照を持ちますthis。あなたはそれを確認することができますjavap

$ javap -private Outer.Inner1
Compiled from "Outer.java"
class Outer$Inner1 extends Outer$InnerBase{
    final Outer this$0;
    private Outer$Inner1(Outer);
}

$ javap -private Outer.InnerBase
Compiled from "Outer.java"
class Outer$InnerBase extends java.lang.Object{
    final Outer this$0;
    private Outer$InnerBase(Outer);
    Outer$InnerBase(Outer, Outer$1);
}

ところで、本当に必要な場合は、いくつかのあいまいなJava構文を利用してthis$0、親クラスと子クラスの間でメンバーの値を変えることができます。方法は次のとおりです。

public class Outer {
    class InnerBase {
        Outer innerBaseOuter() { return Outer.this; }
    }

    class Inner1 extends InnerBase {
        public Inner1() {}
        public Inner1(Outer parentOuter) {
            parentOuter.super(); // set a different super in InnerBase
        }
        Outer innerOuter() { return Outer.this; }
    }

    public static void main(String[] args) {
        Outer outer1 = new Outer();
        Outer outer2 = new Outer();

        Inner1 a = outer1.new Inner1();
        System.out.println("Checking (a.base == a.inner) => "+
            (a.innerBaseOuter() == a.innerOuter()));

        Inner1 b = outer1.new Inner1(outer2);
        System.out.println("Checking (b.base == b.inner) => "+
            (b.innerBaseOuter() == b.innerOuter()));
    }
}

あなたが得るプログラムを実行する:

Checking (a.base == a.inner) => true
Checking (b.base == b.inner) => false
于 2012-04-16T19:57:11.133 に答える
1

それぞれに、Outerの同じインスタンスへの参照があります。それぞれにOuterへの独自の参照がありますが、その参照はOuterの同じインスタンスを指します。参照は変更できないので、それらのいずれかから外部これを参照することは同等です。

class Outer {
    private class Inner1 extends InnerBase {
        Outer getOuter() {
            return Outer.this;
        }
    }

    private class Inner2 extends InnerBase {
        Outer getOuter() {
            return Outer.this;
        }
    }

    private class InnerBase {
        Outer getOuter() {
            return Outer.this;
        }
    }

    public static void main(String[] args) {
        new Outer().test();
    }
    public void test() {
        System.out.println((new Inner1()).getOuter());
        System.out.println((new Inner2()).getOuter());
        System.out.println((new InnerBase()).getOuter());
    }
}
于 2012-04-16T20:05:22.317 に答える
1

プログラムを見てみましょう:

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、プログラムの機能を実際に理解できます。

要約すると、あるクラスが別のクラスの内部クラスとサブクラスの両方になることはめったに適切ではありません。より一般的には、内部クラスを拡張することが適切になることはめったにありません。必要に応じて、囲んでいるインスタンスについてじっくり考えてください。ほとんどのメンバークラスは、静的として宣言でき、宣言する必要があります。

于 2012-04-16T20:25:56.267 に答える
0

あなたの例には「内部」のレベルが1つしかないため、各内部クラスはOuter、あなたが言及したようにクラスにアクセスできます。

楽しみのための例を次に示します。

public class Test {

    public static void main(String[] args) {
        Level1 l1 = new Level1();
        Level1.Level2 l2 = l1.new Level2();
        Level1.Level2.Level3 l3 = l2.new Level3();
    }

    public static class Level1 {

        private String s = "Level1";

        public Level1() {
            System.out.println(this + ": " + s);
        }

        public class Level2 {

            private String s = "Level2";

            public Level2() {
                System.out.println(this + ": " + s);
                System.out.println("Level1: " + Level1.this);
            }

            public class Level3 extends OtherLevel {

                private String s = "Level3";

                public Level3() {
                    System.out.println(this + ": " + s);
                    System.out.println("Level1: " + Level1.this);
                    System.out.println("Level2: " + Level2.this);

                    System.out.println("super: " + super.toString());
                }
            }
        }

        public class OtherLevel {

            private String s = "OtherLevel";

            public OtherLevel() {
                System.out.println(this + ": " + s);
            }
        }
    }
}

出力:

javaapplication4.Test$Level1@70284ac3: Level1
javaapplication4.Test$Level1$Level2@74a14fed: Level2
Level1: javaapplication4.Test$Level1@70284ac3
javaapplication4.Test$Level1$Level2$Level3@88d00c6: OtherLevel
javaapplication4.Test$Level1$Level2$Level3@88d00c6: Level3
Level1: javaapplication4.Test$Level1@70284ac3
Level2: javaapplication4.Test$Level1$Level2@74a14fed
super: javaapplication4.Test$Level1$Level2$Level3@88d00c6
于 2012-04-16T19:58:39.577 に答える