Stream APIの特殊なメソッドとメソッド参照/コンストラクター参照:Java 8はラムダ式でここまで変わる(終)(4/4 ページ)
本連載では、今までJavaの経験はあっても「ラムダ式は、まだ知らない」という人を対象にラムダ式について解説していきます。最終回は、Stream APIの特殊なメソッド3つと、ラムダ式と関係が深いメソッド参照やコンストラクター参照についてコード例を交えて解説。
コラム「今後の課題:ラムダ式とメソッド参照の使い分け」
ここまでラムダ式とともにメソッド参照を見てきたのですが、メソッド参照を使うと引数を省略できる分、基本的には記述量が減るのが分かるかと思います。しかし、可読性という点から見たらどうでしょう?
例えば、先ほど紹介した引数を3つ持つStream#collectメソッドの次の処理があったとします。
List<Person> list = new ArrayList<>(); list.add(new Person("テスト", "太郎")); list.add(new Person("テスト", "次郎")); list.add(new Person("テスト", "花子")); list.add(new Person("サンプル", "小太郎")); list.add(new Person("サンプル", "小次郎")); list.add(new Person("サンプル", "華子")); // --- メソッド参照の場合 --- List<Person> methodRefList = list.stream().collect( () -> new ArrayList<Person>(), // コンストラクター参照は次項で説明します ArrayList::add, ArrayList::addAll); System.out.println("methodRefList = " + methodRefList);
これのcollectメソッドの部分をラムダ式で書き換えると次のようになります。
List<Person> lambdaList = list.stream().collect( () -> new ArrayList<Person>(), (List container, Person element) -> container.add(element), (List container1, List container2) -> container1.addAll(container2));
これらを比べてみると、引数に型を指定したり引数名に名前を付けたりすることで、「メソッド参照よりラムダ式の方が何をしているのかが少しは分かりやすい」と思う人も、「メソッド参照の方がすぐ理解できる」と思う人もいることでしょう。
このように、「ラムダ式で記述する方がよいのか」「メソッド参照で記述する方がよいのか」「省略はどこまで行った方がよいのか」など、そのようなことに対して開発の規模、開発者のレベル、システムに携わる人の立ち位置によっても変わってくるので、何が最良になるのか判断が難しいところでもあります。
今後、Java 8の新機能を使われていくにつれ、どのようなときにラムダ式またはメソッド参照を使うのが効率的なのかについては、今後はさまざまな場合の指針を作り、その指針が認知されるようにすることが課題になるのかもしれません。
コンストラクター参照
コンストラクター参照はクラスをインスタンス化される際のコンストラクターを参照するものです。書式はメソッド参照のようにクラス名と「new」を「::」でつなげた下記の書式になります。
クラス名::new
またコンストラクター参照はクラスだけではなく配列でも使うことが可能です。
それでは、サンプルを見てみましょう。このサンプルでは数値の値を持つ文字列のStreamからクラスのコンストラクター参照を呼んでBigDecimalのインスタンスを作っています。また、同じ文字列のStreamからString配列のコンストラクター参照を呼んでString配列を作成しています。
public class ClassReferenceSample { public static void main(String[] args) { List<String> list = Arrays.asList("1.0", "2.0", "3.0"); // --- クラスの場合 --- // ラムダ式 System.out.println("--- [1] ラムダ式 ---"); list.stream() .map(value -> new BigDecimal(value)) .forEach(value -> System.out.println(value.getClass() + ": " +value)); // コンストラクター参照 System.out.println("--- [2] コンストラクター参照---"); list.stream() .map(BigDecimal::new) .forEach(value -> System.out.println(value.getClass() + ": " +value)); // --- 配列の場合 --- // ラムダ式 System.out.println("--- [3] ラムダ式 ---"); String[] lambdaValues = list.stream().toArray(size -> new String[size]); for (String value: lambdaValues){ System.out.println(value.getClass() + ": " +value); } // コンストラクター参照 System.out.println("--- [4] コンストラクター参照---"); String[] constRefValues = list.stream().toArray(String[]::new); for (String value: constRefValues){ System.out.println(value.getClass() + ": " +value); } } }
--- [1] ラムダ式 --- class java.math.BigDecimal: 1.0 class java.math.BigDecimal: 2.0 class java.math.BigDecimal: 3.0 --- [2] コンストラクター参照--- class java.math.BigDecimal: 1.0 class java.math.BigDecimal: 2.0 class java.math.BigDecimal: 3.0 --- [3] ラムダ式 --- class java.lang.String: 1.0 class java.lang.String: 2.0 class java.lang.String: 3.0 --- [4] コンストラクター参照--- class java.lang.String: 1.0 class java.lang.String: 2.0 class java.lang.String: 3.0
Java特有のベストプラクティスなども作っていこう
本連載ではJava 8で導入されたラムダ式を中心に、それらと密接に関係するJava 8の新機能について見てきました。そして今回のJava 8でのバージョンアップで、今まで以上に大きな仕様の追加があったことが分かったかと思います。そして、これらの新機能を使いこなすためにも、まずはラムダ式の書式について知っておかないといけません。
そして、ラムダ式は今までのJavaの表記法とは大幅に違うので、前知識なしでラムダ式が記述されたソースコードを見ても、理解するのにそれなりの時間がかかるかと思います。そのため、Javaを使わないといけない人は時間があるときにでもラムダ式とJava 8の新機能については目を通しておいた方がよいでしょう。
また、今回のJava 8のような大幅な変更があった場合は、便利になった分、バグの原因になりやすいなどの理由で、可能でもやらない方がいいことなども出てきます。ラムダ式やそれに関わる新機能はすでに他の言語で使われているものも多いので、それら他の言語のノウハウなどを共有することや、Java特有のベストプラクティスなども作っていくことが今後必要になってくることかと思います。
著者プロフィール
長谷川 智之(はせがわ ともゆき)
株式会社ビーブレイクシステムズ開発部所属。
社内サークル執筆チーム在籍。
主な執筆。
@IT連載『Javaの常識を変えるPlay framework入門』
日経ソフトウェア連載『コツコツ学ぶAndroidネイティブアプリ開発教室』
Copyright © ITmedia, Inc. All Rights Reserved.
関連記事
- JavaOne Tokyo 2012まとめレポート(後編):ラムダ式、JAR脱獄、JavaScript/■■Node.jsに接近するJDK 8、そして9へ
JDK 8の新機能のうち、Lambda、Jigsaw、Nashorhについて解説した講演を詳細にレポートする。そしてJava SE 9はどうなる? - スケーラブルで関数型でオブジェクト指向なScala入門(4):基本的なパターンマッチとScalaで重要な“関数”
match構文、関数の定義と呼び出し、高階関数、プレイスホルダ構文、部分適用、クロージャなどを紹介します - 「Javaに並列処理と関数型言語の要素を」、ティム・ブレイ氏
- いよいよ始まるRuby 1.9への移行:関数型っぽくなったRuby 1.9
- WebSocketやREST APIのサポート強化:Java 8&Java EE 7に対応した「Spring Framework 4.0」正式版リリース
米Pivotalは2013年12月12日、オープンソースのJavaアプリケーションフレームワーク「Spring Framework 4.0」の正式版をリリースした。 - JDK 8、TLS 1.2がデフォルトに
Javaの通信暗号化もTSL 1.2に。基本的に後方互換性は維持するが、一部影響がある場合もあるという。 - .NET開発『虎の巻』:C#ラムダ式 基礎文法最速マスター
ラムダ式(C#)の基礎文法を、短い説明と簡単なコードで簡潔にまとめる。「ラムダ式、どう書くんだっけ?」という場合の簡易リファレンスとして活用できる - .NET開発『虎の巻』:VBラムダ式 基礎文法最速マスター
今度はVB。ラムダ式の基礎文法を、短い説明と簡単なコードでまとめる。「ラムダ式、どう書くんだっけ?」という場合の簡易リファレンスとして活用できる - 特集:C#開発者のためのF#入門(前編):F#で初めての関数型プログラミング
アルゴリズム実装などに威力を発揮する、関数型プログラミングの基礎やF#言語の特徴を解説。C#開発者なら、ここから始めよう! - Gaucheでメタプログラミング(1):ちょっと変わったLisp入門
Lispの一種であるScheme。いくつかある処理系の中でも気軽にスクリプトを書けるGaucheでLispの世界を体験してみよう