以下のコードは、出力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");
}
}
}
_
ローカルクラスのメソッドが呼び出されることを期待していたと思います。ローカルクラスのスコープ外でnew A()
を使用しているため、これは発生しませんでした。したがって、スコープ内で次に近い候補、つまり内部クラスにアクセスします。から JLS§6. :
ブロック(§14.2)ですぐに囲まれたローカルクラス宣言のスコープは、それ自体のクラス宣言を含む、すぐに囲まれたブロックの残りの部分です。
したがって、メソッドの最初の行のnew A()
は、その後に表示されるローカルクラスにアクセスしません。その前にクラス宣言を移動すると、期待どおりの出力が得られます。
同様の例が含まれている JLS§14. も参照してください。
コードの順序が原因で、出力が「中間」になっています。メソッドスコープの_class A
_が発生するためafternew A()
の呼び出しにより、出力は「中間」になります。次のように順序を切り替えると、「内部」の出力が得られます。
_void go() {
class A {
void m() {
System.out.println("inner");
}
}
new A().m();
}
_
出力:
inner
_class A
_を高から低にインスタンス化するための優先順位は、次のとおりです。
詳細については、公式 内部クラスについて説明しているJava言語仕様 を参照してください。
inner
が出力されない理由は( 6. ):
ブロックですぐに囲まれるローカルクラス宣言のスコープは、それ自体のクラス宣言を含む、すぐに囲むブロックの残りの部分です。
(メソッド内で宣言されたクラスはローカルクラスと呼ばれます。)
したがって、式new A()
は宣言の前に発生するため、A
はローカルクラスを参照できません。言い換えると、ローカルクラスはローカル変数と同様のスコープを持っています。
middle
の代わりにouter
が出力される理由は、内部クラスA
shadowsが上部にあるためです。 -レベルクラスA
( 6.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;
}
}
基本的に同じことがクラス宣言でも起こっています。
ケース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
が出力されます。
メソッドで:
_ 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
になります。
go
のインスタンスを使用してMethodLocalVSInner
メソッドを呼び出しています
Goメソッド内では、ここでA()
のインスタンスを作成しています。これは、外部のA class
を明示的にインポートしておらず、直接の内部クラスがメソッド呼び出しステートメントの後にあるため、JVMはのクラスレベルにあるinner class A
を選択しています。 MethodLocalVSInner
そしてその中でgoメソッドを実行します