16

私はOuterクラスを持つクラスを持っていprivate Innerます。

私のOuterクラス メソッドでは、Inner次のようにクラスをインスタンス化します。

Outer outer = new Outer();
Inner inner = outer.new Inner();

コンパイラは、このコードを次のように変換します。

Outer outer = new Outer();
Inner inner = new Inner(outer, null);

リフレクションを使用すると、Innerクラスに次の合成コンストラクターがあることがわかります。

private Outer$Inner(Outer)
Outer$Inner(Outer,Outer$Inner)

Innerクラスはであるためprivate、コンパイラはそのprivateコンストラクタをそれに追加して、誰もそのクラスをインスタンス化できないようにします。しかし、明らかに、Outerクラスはそれをインスタンス化できるはずなので、コンパイラは、プライベート コンストラクターを呼び出す他のパッケージ プライベート コンストラクターを追加します。また、package-private コンストラクターの$名前にはそれが含まれているため、通常の Java コードでは呼び出すことができません。

質問: 1 つのプライベート コンストラクターと 1 つのパッケージ プライベート コンストラクターを合成するのはなぜですか? package-private コンストラクターのみを合成して、それで完了しないのはなぜですか?

4

3 に答える 3

13

のようにコードを書くと、

public class Outer {
      private class Inner {}
}

コンストラクターが 1 つしかないことに注意してください。private Outer$Inner(Outer)

このコンストラクターはJLS のセクション 8.8.9 で必要とされ、コンストラクターが定義されていない場合はデフォルトのコンストラクターを生成する必要があり、この場合、デフォルトのコンストラクターはプライベートである必要があると規定されています。

クラス型では、クラスが public と宣言されている場合、既定のコンストラクターには暗黙的にアクセス修飾子 public が与えられます (§6.6)。クラスが保護されていると宣言されている場合、デフォルトのコンストラクターには暗黙的にアクセス修飾子 protected が与えられます (§6.6)。クラスが private と宣言されている場合、デフォルトのコンストラクターには暗黙的にアクセス修飾子 private が与えられます (§6.6)。それ以外の場合、既定のコンストラクターは、アクセス修飾子なしで暗黙的に既定のアクセスを持ちます。

ただし、次のようなコードで Inner のインスタンスを Outer 内でインスタンス化すると、

public class Outer {
    private class Inner {}
        public String foo() {
            return new Inner().toString(); 
        }
}

コンパイラは、Outer が合法的に呼び出すことができるコンストラクターを生成する必要があります (プライベートなデフォルト コンストラクターはプライベートであるため、合法的に呼び出すことはできません)。そのため、新しい合成コンストラクターをコンパイラーによって生成する必要があります。JLS のセクション 13.1 によると、新しいコンストラクターは合成でなければなりません。

コンパイラによって導入され、ソース コードに対応する構造がない構造は、既定のコンストラクタとクラスの初期化メソッドを除き、合成としてマークする必要があります。

この 2 番目のコンストラクターには、ソース コード内に対応するコンストラクトがないため、この新しいコンストラクターは合成でなければなりません。JLS にはプライベートのデフォルト コンストラクターが必要なため、最初のプライベート コンストラクターを生成する必要があります。

于 2013-03-08T06:03:56.703 に答える
3

これは答えではなく、sbridgesで十分にカバーされていると思います。これは、説明した動作を生成する単なる実例です。

public class Outer {
    private class Inner {
    }

    public static void main(String[] args) {
        printConstructors();

        //only one constructor is printed but two would appear if you 
        //uncommented the line below

        //new Outer().new Inner();
    }

    private static void printConstructors() {
        Constructor[] constructors = Outer.Inner.class.getDeclaredConstructors();
        for (Constructor c : constructors) {
            System.out.println(c.toGenericString());
        }
    }
}
于 2013-03-08T10:20:34.367 に答える
2

最も可能性の高い答えは、ソース コードで宣言した内容を尊重することです。これを行うと、宣言したとおりにリフレクションによってプライベートコンストラクターを使用できます。

Innerこれにより、クラス内でプライベート コンストラクターが実際に呼び出されているかどうかのチェックも回避されます。

于 2013-03-04T10:33:25.253 に答える