Java8 がリリースされてから1年以上経過しているので Java プログラマの皆様はガシガシとその恩恵を受けた素晴らしいコードを書いていらっしゃるでしょう。
私は未熟者なのでボチボチとチマチマ楽しんでいます。(^_^;)
そこで問題です。
このプログラムを実行すると何が表示されますか?
package jp.yucchi.hellorepeat;
import java.util.Arrays;
import java.util.List;
public class HelloRepeat {
public static void main(String[] args) {
List list = Arrays.asList("Hello", "World!");
for (int i = 0; i < list.size(); i++) {
Runnable r = () -> System.out.println(list.get(i));
r.run();
}
}
}
1. Hello
World!
2. World!
Hello
3. コンパイルエラー
4.実行時エラー
すいません。
簡単すぎますね。
答えは 3番のコンパイルエラーです。
ラムダ式の中から外部の変数を参照する場合、その変数はfinalもしくは実質的finalでなければならない。
このルールに違反しているからです。
どうすればこのプログラムを正しく動かせるでしょうか?
それも簡単なので答えを載せておきますね!
このプログラムは for ループ文を使っているのでまずは、拡張 for ループ文で修正してみましょう。
拡張 for ループ文は少しばかりの制約はあるけど要素を先頭から順に取得するような処理には便利に使えます。
for ループ文ではインデックス取得の為に実質的ファイナルでない変数にアクセスしてコンパイルエラーになりましたが拡張 for ループ文だとそのような心配はありません。
for (String str : list) {
Runnable r = () -> System.out.println(str);
r.run();
}
あっけなく、簡単に修正できました。それでは、default void forEach(Consumer<? super T> action) を使って修正してみます。
list.forEach(e -> {
Runnable r = () -> System.out.println(e);
r.run();
}
);
こちらの方法が Java8 っぽいので個人的には好きです。
でも内部的には、デフォルト実装の動作は次のようになっているそうです。
for (T t : this)
action.accept(t);
つまり、こういうことなのかな?
for (String str : list) {
new Consumer() {
@Override
public void accept(String e) {
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println(e);
}
};
r.run();
}
}.accept(str);
}
以上のことを考慮してラムダ式使う場合は、forEach(Consumer<? super T> action) を使うのが良さそうですね。
これで平凡なパズルはお終いです。