web-dev-qa-db-ja.com

メソッドローカル内部クラスと内部クラス

以下のコードは、出力middleを生成します。誰かがこれがどのように起こっているかを詳細に説明できますか?

_class A_の「内部」バージョンの宣言は、go()メソッドで_class A_のインスタンスが作成された後に行われるためですか?

_class A {
    void m() {
        System.out.println("outer");
    }
}

public class MethodLocalVSInner {
    public static void main(String[] args) {
        new MethodLocalVSInner().go();
    }

    void go() {
        new A().m();
        class A {
            void m() {
                System.out.println("inner");
            }
        }
    }

    class A {
        void m() {
            System.out.println("middle");
        }
    }
}
_
44
Ram Patra

ローカルクラスのメソッドが呼び出されることを期待していたと思います。ローカルクラスのスコープ外でnew A()を使用しているため、これは発生しませんでした。したがって、スコープ内で次に近い候補、つまり内部クラスにアクセスします。から JLS§6.

ブロック(§14.2)ですぐに囲まれたローカルクラス宣言のスコープは、それ自体のクラス宣言を含む、すぐに囲まれたブロックの残りの部分です。

したがって、メソッドの最初の行のnew A()は、その後に表示されるローカルクラスにアクセスしません。その前にクラス宣言を移動すると、期待どおりの出力が得られます。

同様の例が含まれている JLS§14. も参照してください。

38
Rohit Jain

コードの順序が原因で、出力が「中間」になっています。メソッドスコープの_class A_が発生するためafternew A()の呼び出しにより、出力は「中間」になります。次のように順序を切り替えると、「内部」の出力が得られます。

_void go() {
    class A {
        void m() {
            System.out.println("inner");
        }
    }
    new A().m();
}
_

出力:

inner

_class A_を高から低にインスタンス化するための優先順位は、次のとおりです。

  1. ブロック
  2. 方法
  3. クラス
  4. パッケージ

詳細については、公式 内部クラスについて説明しているJava言語仕様 を参照してください。

17
Tim Biegeleisen

innerが出力されない理由は( 6. ):

ブロックですぐに囲まれるローカルクラス宣言のスコープは、それ自体のクラス宣言を含む、すぐに囲むブロックの残りの部分です。

(メソッド内で宣言されたクラスはローカルクラスと呼ばれます。)

したがって、式new A()は宣言の前に発生するため、Aはローカルクラスを参照できません。言い換えると、ローカルクラスはローカル変数と同様のスコープを持っています。

middleの代わりにouterが出力される理由は、内部クラスAshadowsが上部にあるためです。 -レベルクラスA6.4.1 ):

dという名前の型の宣言nは、nのスコープ[…]にあるdという名前の他の型の宣言をシャドウします。

これは、MethodLocalVSInnerの本体のどこでも、修飾されていないAが内部クラスを参照する必要があることを意味します。

メンバー変数のシャドウイングに精通している場合(例:

class Example {
    int x;
    void setX(int x) {
        //       ┌ 'x' refers to the local method parameter
        this.x = x;
    }
}

基本的に同じことがクラス宣言でも起こっています。

7
Radiodef

ケース1:

void go() {
        new A().m();
        class A {
            void m() {
                System.out.println("inner");
            }
        }
    }

この場合、ローカルクラスのスコープ外でメソッドを実行している場合。そのため、middleが出力されます

ケース2:

void go() {
        class A {
            void m() {
                System.out.println("inner");
            }
        }
        new A().m();
    }

この場合、クラスがスコープ内にあるため、innerが出力されます。

4
Sumit Singh

メソッドで:

_ void go() {
    new A().m();
    class A {
        void m() {
            System.out.println("inner");
        }
    }
}
_

メソッドの実行が開始されると、最初の行が実行されますnew A().m();

内部クラスはすでにスコープ内にあるため、そのクラスのオブジェクトが作成され、_inner class_ではなく_local method class_に対してmメソッドが呼び出されます。これはまだスコープ内にないためです。そのため、出力としてmiddleを取得しています。

ただし、メソッドを次のように変更した場合:

_ void go() {

    class A {
        void m() {
            System.out.println("inner");
        }
    }
   new A().m();
}
_

これで、ローカルメソッドクラスがスコープ内にあり、優先度が高くなるため、出力がinnerになります。

2
Prashant

goのインスタンスを使用してMethodLocalVSInnerメソッドを呼び出しています

Goメソッド内では、ここでA()のインスタンスを作成しています。これは、外部のA classを明示的にインポートしておらず、直接の内部クラスがメソッド呼び出しステートメントの後にあるため、JVMはのクラスレベルにあるinner class Aを選択しています。 MethodLocalVSInnerそしてその中でgoメソッドを実行します