「Java 8 Lambdas」を読む会第2回を実施して
昨日はJava読書会BOF主催のJava読書会「Java 8 Lambdas」を読む会の第2回を開催しました。洋書にも関わらず、10人を超える参加者が集まりました。
今回の読書範囲で感じたことなど
ラムダ式の引数命名
ラムダ式の記述で、引数の変数名に著者は多少長くなっても意味を示す名前を付けています。
Example 3-16より引用
int count = Stream.of(1, 2, 3) .reduce(0, (acc, element) -> acc + element);
たまにラムダ式の解説でみかける1文字変数のコード例だと
int count = Stream.of(1, 2, 3) .reduce(0, (a, e) -> a + e);
と短くなるものの、'a'って? 'e'って? となって可読性が下がります。
for文、分かりやすいではないか
上記のreduceを使ったサンプルコードを手続き的にfor文で書いた例が記載されています。
Example 3-18より引用
int acc = 0; for (Integer element : asList(1, 2, 3)) { acc = acc + element; }
ループを制御し、変数(acc)の更新を手動で管理しなければいけないと書かれてはいるものの、現時点ではこの短いサンプルではfor文の方が分かりやすいと感じてしまいました。もっとStream APIに慣れていかないとと思いました。
ListやSetを返すドメインクラスはレガシー
ドメインクラスに何かのリストを返すメソッドを定義するとき、戻り値の型をListやSetではなくStreamにすることを推奨しています。ドメインモデルのデータ構造をよりよくカプセル化するという根拠です。読書会の場でもここについて議論が活発に繰り広げられました。
ドメインクラスが内部に抱えるデータをListやSetで返す場合、外部からデータを変更されないようにするため、
return Collections.unmodifiableList(musicians);
のようにするとか、コピーしたものを返すとかする必要があります。
Streamを返すとこのような防衛的プログラミングが不要になるのは確かにメリットです。
ただ、Setのように型に意図を持たせたコードがStreamでは意図が曖昧になってしまうというデメリットもあります。
ロギングの性能とラムダ式
ロギングでは、ログレベルが無効なときにもログ出力文字列生成処理が実行されてしまうので、それを防ぐ手段としてラムダ式が有効です。
Example 4-2より引用
Logger logger = new Logger(); logger.debug(() -> "Look at this: " + expensiveOperation());
このラムダ式は、デバッグレベルのログ出力が有効なときにのみ実行されるので効率がよいというものです。このコードで使っているLoggerなどは特定のログAPIではなく一般論で書かれていますが、Java SE 8のjava.util.loggingでは実際にラムダ式(関数型インタフェース)を引数に取るメソッドが用意されているので、すぐに適用することができます。
ここで、ラムダ式を使うと毎回インスタンスが生成されるのでは?という議論になり、その場では解決できませんでした。
ラムダ式がどのようなコードになるのか? については、Brian Goetz氏のJavaOne 2013 SFのセッション「Lambda: A Peek Under the Hood」の公開資料を探してみるのがよいらしいとJava Day Tokyo 2014で聞いたので、探してみたところ動画コンテンツがありました。
http://parleys.com/play/5251c164e4b0a43ac1212459/about
ちょっと難しめですね。日本語の資料では、2013年夏のJJUGナイトセミナーの資料がよさそうです。
http://www.slideshare.net/miyakawataku/lambda-meets-invokedynamic
キャプチャーをしないラムダ式では、関数型インタフェースのインスタンスが内部でシングルトン・パターンで実装されているので毎回の生成はないですが、キャプチャがあるとインスタンス生成が毎回となるようです。