ラムダ式で this が指すもの
java8からはラムダ式が使えて便利です。
この便利なラムダ式、匿名クラスのシンタックスシュガーのようにも思えるのですが、実際にはそうではないらしいです。(そのあたりの詳細は各々ググってください)
ただのシンタックスシュガーでない事が理由なのかわかりませんが、匿名クラスを使ったコードをラムダ式に書き換えた(書き換えようとした)場合に違いがありましたので、この記事ではそれを取り上げます。
匿名クラスを使ったコードとラムダ式を使ったコードを比較してみます。
匿名クラスを使ったコード
匿名クラスを使ったコードです。(これをラムダ式で書き換えます)
public class Hoge { public static void main(String[] args) { new Hoge().exec(); } void exec() { String msg = "Hollo, World"; Consumer<String> c = new Consumer<String>() { @Override public void accept(String msg) { Object ths = this; System.out.println(ths == Hoge.this); // => false System.out.println(msg); // => Hello, World } }; c.accept(msg); } }
ラムダ式で書き換えようとしたコード
NetBeansでは匿名クラスが"this"を参照している場合は、ラムダ式に変換できません。なので、自分で書き換えました。(※注意。書き換えはうまくいっていません)
public class Hoge { public static void main(String[] args) { new Hoge().exec(); } void exec() { String msg = "Hollo, World"; Consumer<String> c = (msg1) -> { // "msg" は宣言済みで使えない Object ths = this; System.out.println(ths == Hoge.this); // => true System.out.println(msg1); // => Hello, World }; c.accept(msg); } }
ラムダ式の引数の変数名に msg を使おうとすると、宣言済みで使えません。
"this" は Hoge.this を指すので、結果が変わっています。
余談ですが、引数がひとつだけのケースなので、(msg1) は msg1 とも書けます。
ラムダ式では、"this" の参照が匿名クラスとは異なるので困りました。
では、どのようにすれば匿名クラスと同じ "this" を参照できるのでしょうか。
ちょっと書いてみました。
以下は正しい結果になります。
ラムダ式で自身を参照する
インスタンス変数にしてしまうやり方
public class Hoge { public static void main(String[] args) { new Hoge().exec(); } Consumer<String> c = (msg1) -> { Object ths = this.c; // Hoge.this.c でも同じ System.out.println(ths == Hoge.this); // => false System.out.println(msg1); // => Hello, World }; void exec() { String msg = "Hollo, World"; c.accept(msg); } }
ただし、ラムダ式は初めて実行された時にクラスが生成されるらしいので、この方法だとその恩恵が受けにくくなります。
ローカルに参照を残すやり方
public class Hoge { public static void main(String[] args) { new Hoge().exec(); } void exec() { String msg = "Hollo, World"; Consumer<String> c; { Consumer<String>[] ref = (Consumer<String>[]) new Consumer<?>[1]; c = (ref[0] = (msg1) -> { Object ths = ref[0]; // ローカル変数から取り出す System.out.println(ths == Hoge.this); // => false System.out.println(msg1); // => Hello, World }); } c.accept(msg); } }
かなり面倒です。さらに、ラムダの外部の変数を参照することになるので、効率化は望めません。(ラムダ式が外部の変数を参照していなければ、シングルトン化されるらしいです)