web-dev-qa-db-ja.com

ネストされた子クラスが親クラスのプライベートメンバーにアクセスできるのに、孫はアクセスできないのはなぜですか?

おそらく質問に似ています なぜ外部Javaクラスは内部クラスのプライベートメンバーにアクセスできますか? または サブクラスでsuperキーワードを使用してスーパークラスのプライベートフィールドにアクセスできますか?

ただし、いくつかの違いがあります。childrenクラスは、親(および最も近いparentのみ)クラスのprivateメンバーにアクセスできます。

以下のサンプルコードを考えます。

public class T {

    private int t;

    class T1 {
        private int t1;

        public void test() {
            System.out.println(t);
        }
    }

    class T2 extends T1 {

        private int t2;

        public void test() {
            System.out.println(t);
            System.out.println(super.t1);
            System.out.println(this.t2);
        }
    }

    class T3 extends T2 {

        public void test() {
            System.out.println(t);
            System.out.println(super.t1); // NG: t1 Compile error! Why?
            System.out.println(super.t2); // OK: t2 OK
        }
    }
}
50
andyf

合成アクセサメソッド

技術的には、 [〜#〜] jvm [〜#〜] レベルでは、[〜#〜] not [〜#〜]にアクセスできます別のクラスのprivateメンバー—外側のクラス(_T.t_)のメンバーでも、親クラス(_T2.t2_)のメンバーでもありません。あなたのコードでは、コンパイラは synthetic にアクセスするクラスのアクセサメソッドを生成するので、just like likeできます。 _T3_クラスで無効な参照を修正した場合も同じことが起こります _super.t1_ 正しい形式_((T1) this).t1_を使用します。

このようなコンパイラが生成したsyntheticアクセサメソッドを使用すると、一般的にアクセスできますanyprivateanyのメンバー外側(最上位)Tクラスにネストされたクラス、例えば_T1_からnew T2().t2を使用できます。これは_private static_メンバーにも適用されることに注意してください。

synthetic属性は、 [〜#〜] jdk [〜#〜] リリース1.1で導入され、新しい言語であるネストされたクラスをサポートします。その時の機能Java。その時から。 [〜#〜] jls [〜#〜] は明示的にすべてのメンバーへの相互アクセスを許可します。 privateを含むトップレベルのクラス。

ただし、下位互換性のために、コンパイラーはネストされたクラスをアンラップし(例えば、_T$T1_、_T$T2_、_T$T3_)、privateメンバーを変換しますaccessesを- calls生成されるsyntheticアクセサメソッド(したがって、これらのメソッドには package private が必要です。つまり、 default 、可視性):

_class T {
    private int t;

    T() { // generated
        super(); // new Object()
    }

    static synthetic int access$t(T t) { // generated
        return t.t;
    }
}

class T$T1 {
    private int t1;

    final synthetic T t; // generated

    T$T1(T t) { // generated
        this.t = t;
        super(); // new Object()
    }

    static synthetic int access$t1(T$T1 t$t1) { // generated
            return t$t1.t1;
    }
}

class T$T2 extends T$T1 {
    private int t2;

    {
        System.out.println(T.access$t((T) this.t)); // t
        System.out.println(T$T1.access$t1((T$T1) this)); // super.t1
        System.out.println(this.t2);
    }

    final synthetic T t; // generated

    T$T2(T t) { // generated
        this.t = t;
        super(this.t); // new T1(t)
    }

    static synthetic int access$t2(T$T2 t$t2) { // generated
        return t$t2.t2;
    }
}

class T$T3 extends T$T2 {
    {
        System.out.println(T.access$t((T) this.t)); // t
        System.out.println(T$T1.access$t1((T$T1) this)); // ((T1) this).t1
        System.out.println(T$T2.access$t2((T$T2) this)); // super.t2 
    }

    final synthetic T t; // generated

    T$T3(T t) { // generated
        this.t = t;
        super(this.t); // new T2(t)
    }
}
_

N.B .: syntheticのメンバーを直接参照することは許可されていないため、ソースコードでは使用できません。 int i = T.access$t(new T());自分。

15
charlie

非常に良い発見!私たちは皆、あなたのコード例をコンパイルすべきだと思っていたと思います。

残念ながら、そうではありません... [〜#〜] jls [〜#〜]§15.11.2。 "superを使用したスーパークラスメンバーへのアクセス" (強調鉱山):

フィールドアクセス式super.fがクラスC内にあり、CのimmediateスーパークラスがクラスSであるとします。Sのfがクラスからアクセス可能な場合C(§6.6)の場合、super.fはクラスSの本体の式this.fであるかのように扱われます。そうでない場合、コンパイル時エラーが発生します。

すべてのフィールドが同じ包含クラスにあるため、アクセシビリティが提供されます。プライベートにすることもできますが、アクセス可能です。

問題は、T2immediateT3のスーパークラス)でsuper.t1this.t1として扱うことが違法であるということです- t1にフィールドT2がありません。したがって、コンパイラエラー。

7
Seelenvirtuose