ひしだまの変更履歴

ひしだまHPの更新履歴。
主にTRPGリプレイの元ネタ集、プログラミング技術メモと自作ソフト、好きなゲームや音楽です。

無名内部クラスから見える景色

2016-12-17 15:05:06 | PG(Java)

bitter_foxさんが、Java Puzzlers Advent Calendar 2016の14日目としてnesting printhingという記事を書いています。
Javaのバージョンによってコンパイルできるかどうかが異なる(JDK1.7だけバグっていてコンパイルできる)という意表を突いた問題で、このバグがどういうときに起こるかというのもなかなか理解が大変でした。
が、それよりも気になったのが、コンパイルできる状態にした上で、なぜそのような挙動をするのかというところです。
簡単に書くと、以下のようなソースですね。

class Main {
	public void print() {
		System.out.println("main");
	}

	public class Inner {
	}

	public void run() {
		new Main() {
			@Override
			public void print() {
				System.out.println("overrideMain");
			}
		}.new Inner() {
			public void test() {
				print(); // Main.this.print();とも書ける
			}
		}.test();
	}
}

これをnew Main().run();として実行すると「main」が表示されます!
new Innerのレシーバーとして、new Mainで無名内部クラス(匿名クラス)を作ってprintメソッドをオーバーライドしているので、そちらが呼ばれそうな気がするのですが、そうはなりません。

元のbitter_foxさんの記事によれば、testメソッドの中は「Main main = Main.this; main.print();」と書けるからMainが呼ばれるんだよ…という感じなんですが、それでも納得いきません。
普通であれば、変数に宣言されたクラスがどうであれ、実際のインスタンスのクラスでオーバーライドされているメソッドが呼ばれるわけですから。


結局のところ、ポイントは、new Inner(無名内部クラス(匿名クラス))から見える範囲(スコープ)はどこか、という話のようです。
単なる内部クラス(Inner)であれば、外側のクラスであるMainのメソッドを呼び出すことが出来ます。
しかし無名内部クラス(new Inner)の場合、見える範囲は、その無名内部クラスが属しているメソッド(上の例ではrunメソッド)の範囲になるようです。
で、runメソッドが属しているのはMainクラスなので、runから見えるprintというのは、Mainクラスのprintメソッドを指すことになります。(new Mainに何が書かれていても、new Innerの中からは見えない)

なので、以下のように「new Innerが入っているメソッド」をMain以外のクラスに置くと、printメソッドは呼び出せなくなります。(コンパイルエラーになる)

class Another {
	public void run() {
		new Main().new Inner() {
			public void test() {
				print(); // コンパイルエラー
				Main.this.print(); // コンパイルエラー
			}
		}.test();
	}
}

なお、以下のようにInnerの中でMainのprintメソッドを呼んでいれば、Mainを継承した無名内部クラスを作った場合にはそのメソッドが素直に呼ばれます。

class Main {
	public void print() {
		System.out.println("main");
	}

	public class Inner {
		public void test() {
			print();
		}
	}

	public void run() {
		new Main() {
			@Override
			public void print() {
				System.out.println("overrideMain");
			}
		}.new Inner().test();
	}
}

いやあ、いろいろ知らないことがあって、秀逸な問題でした。


コメント    この記事についてブログを書く
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする
« Asakusa Framework GroupSort... | トップ | 複数ジョブフローの並列実行 »
最新の画像もっと見る

コメントを投稿

PG(Java)」カテゴリの最新記事