セミコロンレスJavaはJava8の夢を見るか?

こんにちは.

続編の変数についてはこちら

ボケーッとセミコロンレスJavaの事を考えていたら,そういえばStreamAPIはセミコロンレスJavaの技法にかなり影響を与えるのではと思いはじめました.
というのも,基本的にStreamAPIで書いたものをほぼそのままセミコロンレスJavaとして書くことができるのです.
StreamAPIで書いたものを使えるということはループを読みやすい形でセミコロンレスJavaでも書くことが出来ます.

セミコロンレスJavaについてはなぎせさんの記事を参照してください.
http://d.hatena.ne.jp/Nagise/20120407/1333767870

また,StreamAPIを使わないJava8時代のセミコロンレスJavaもなぎせさんが既に書かれているのでそちらを参照してください.
http://d.hatena.ne.jp/Nagise/20131111/1384170146

セミコロンレスJavaの基本とStreamAPI

簡単な例として0から10までを出力するコードを考えてみます.
今までのセミコロンレスJavaでは以下のように書いていました(main内のみを抜粋).

for (int i : new int[]{0}) {
    while (i <= 10) {
        if (null == System.out.printf("%d\n", i++)) {}
    }
}

変数は拡張for文を応用することで利用できます.

セミコロンレスJavaでは戻り値の型がvoidのメソッドをそのまま呼び出せません.
ですが,戻り値の型がvoidでないメソッドはif文の条件式内で呼び出し,何らかの値と比較することで使用できます.

普通のJavaでStreamAPIを用いて書くと次のようになりますね.

IntStream.rangeClosed(0, 10)
    .forEach(System.out::println);

セミコロンレスJavaではStreamAPIの戻り値がvoidのメソッドは考えずにコードを書いていくことにします.
戻り値がvoidのメソッドといえば先ほど使った終端操作のforEachですね.
セミコロンレスJava with StreamAPIではforEachを別のものに置き換えればこっちのものです.
StreamAPIのイディオムをほぼそのままセミコロンレスJavaでも使うことが出来ます.

さて,forEachと同様の機能を持つ中間操作としてpeekが提供されています.
これは中間操作上の値を覗き見る事が出来ます.
主にデバッグ用途で提供されていますが,セミコロンレスJavaでは実質的にforEachとして機能します.
中間操作のため戻り値は同じStreamです.

IntStream.rangeClosed(0, 10)
    .peek(System.out::println)
    // IntStream

続いて終端操作を呼びださなければ中間操作であるpeekは実行されません.
適当に値を返す終端操作を見つけてあげましょう.
ここでは要素数を返すcountを用います.
countはStreamやプリミティブ型用のIntStreamなど全てのStreamで利用できます.

IntStream.rangeClosed(0, 10)
    .peek(System.out::println)
    .count(); // int

この一連のルールを元に先ほどのコードを書き換えてみます.
まず,forEachで書いていたものをpeekに変えます.
そして,countメソッドを呼び出します.
この式をそのままif文の条件式に持ってきて適当な値と比較します.

if (java.util.stream.IntStream.rangeClosed(0, 10)
    .peek(System.out::println)
    .count() == 0) {}

直感的で読みやすいStreamAPIをほぼそのままセミコロンレスJavaでも使うことが出来ました.

FizzBuzzなんかも簡単に書けます.
Streamで書くと以下のようになりますね.

IntStream.rangeClosed(0, 100)
    .map(i -> i % 15 == 0 ? "FizzBuzz" :
              i % 3 == 0 ? "Fizz" :
              i % 5 == 0 ? "Buzz" : "" + i)
    .forEach(System.out::println);

これもほぼそのままセミコロンレス化できます.

if (java.util.stream.IntStream.rangeClosed(0, 100)
    .map(i -> i % 15 == 0 ? "FizzBuzz" :
              i % 3 == 0 ? "Fizz" :
              i % 5 == 0 ? "Buzz" : "" + i)
    .peek(System.out::println)
    .count() == 0) {}

ラムダ式+StreamAPI,ステキ!