ひしだまの変更履歴

ひしだま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あるある

2016-12-17 00:46:00 | PG(分散処理)

Asakusa Framework Advent Calendar 2016の17日目です。

GroupSortCoGroup演算子では、キー毎にグルーピングされたデータモデルの一覧(List)が渡されてきます。
それらのデータモデルは、デフォルトでは全て別々にインスタンス化されてList内に保持されます。したがって、入力データ量が多いとメモリーが足りなくなってOutOfMemoryErrorになることがあります。
(特によくあるのが、「多くても数件しか無い」と思っていたら、キーがnullのデータが大量にあった!というようなケースですorz)

この場合、GroupSort(やCoGroup)アノテーションにInputBuffer.ESCAPEを設定すると、データモデル1個分しかメモリーを使わなくなります。
データモデル1個分のインスタンスがList内で使いまわされるので、Listから一度に1個しか取得できない(Listから取得した他のデータモデルオブジェクトを保存しておくということが出来なくなる)という制約はありますが。

なお、昨日の『Asakusa on Sparkあるある』で書いたとおり、Asakusa on Sparkでは演算子からの出力データ数が多い場合も問題になることがあります。それへの対処方法は、そちらをご覧下さい。

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする