倭マン's BLOG

くだらない日々の日記書いてます。 たまにプログラミング関連の記事書いてます。 書いてます。

ラムダ式で assert を使うときはブロックにしないといけない?

java.util.function.Consumer インターフェースは引数を1つ持ち、返り値のない (void) 抽象メソッド accept() を持ちます。 例えば以下のようにしてラムダ式を代入することができます:

Consumer<String> cons1 = s -> System.out.println(s);

さて、ここでラムダ式として assert 文を使ったテストを行ってみます:

Consumer<String> cons2 = s -> assert !s.isEmpty();

これをコンパイルすると「リリース1.4から'assert'はキーワードなので、識別子として使用することはできません」というコンパイル・エラーになります・・・ そんなこと知ってらぁw ラムダ式の本体をブロックにすれば問題なくコンパイルできます:

Consumer<String> cons3 = s -> { assert !s.isEmpty(); };

ちなみに無名クラスとして定義しても問題なし:

Consumer<String> cons4 = new Consumer<String>(){
    public void accept(String s){
        assert !s.isEmpty();
    }
};

本体がブロックでないラムダ式で assert 文によるテストが書けないのって、何か理由あるのかな? 単なるバグ?(Java 8 ea b90)

ちなみに何故ラムダ式を使って assert 文によるテストを書こうとしたかというと、Java8 で導入される java.util.stream.Stream インターフェースに定義されている peek() メソッドを使うと Map / Reduce 形式の処理の途中で容易にテストが書けそうだなと思ったからです:

import java.util.stream.*;

Stream<String> stream = 
    Stream.of("Java", "Groovy", "Scala", "Clojure", "Kotlin", "Jython", "JRuby");

stream.flatMap(s -> stringToStream(s))
      .peek(s -> { assert s.length() == 1; })    // キチンと変換されているかテストできる
      .map(s -> s + " ")
      .peek(s -> { assert s.length() == 2 && s.endsWith(" "); })    // テスト
      .forEach(System.out::print);    // 「J a v a G r ・・・ b y」

// 文字列を1文字ずつの Stream へ変換。
public static Stream<String> stringToStream(String s){
    return IntStream.range(0, s.length()).mapToObj(i -> s.substring(i, i+1));
}

中括弧 {} とセミコロン ; 邪魔だよ。 まぁ、JUnit のようなテスティング・フレームワーク使えばいいんだけどね。

追記
java8-ea-b120 でも assert はブロックで書かないとコンパイラに怒られました。 また、上記サンプルの stringToStream() メソッドをストリームばっちり使ってる風に書き換えました。

JUnit実践入門 ~体系的に学ぶユニットテストの技法 (WEB+DB PRESS plus)

JUnit実践入門 ~体系的に学ぶユニットテストの技法 (WEB+DB PRESS plus)