ここまでラムダ式とともにメソッド参照を見てきたのですが、メソッド参照を使うと引数を省略できる分、基本的には記述量が減るのが分かるかと思います。しかし、可読性という点から見たらどうでしょう?
例えば、先ほど紹介した引数を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 8で導入されたラムダ式を中心に、それらと密接に関係するJava 8の新機能について見てきました。そして今回のJava 8でのバージョンアップで、今まで以上に大きな仕様の追加があったことが分かったかと思います。そして、これらの新機能を使いこなすためにも、まずはラムダ式の書式について知っておかないといけません。
そして、ラムダ式は今までのJavaの表記法とは大幅に違うので、前知識なしでラムダ式が記述されたソースコードを見ても、理解するのにそれなりの時間がかかるかと思います。そのため、Javaを使わないといけない人は時間があるときにでもラムダ式とJava 8の新機能については目を通しておいた方がよいでしょう。
また、今回のJava 8のような大幅な変更があった場合は、便利になった分、バグの原因になりやすいなどの理由で、可能でもやらない方がいいことなども出てきます。ラムダ式やそれに関わる新機能はすでに他の言語で使われているものも多いので、それら他の言語のノウハウなどを共有することや、Java特有のベストプラクティスなども作っていくことが今後必要になってくることかと思います。
長谷川 智之(はせがわ ともゆき)
株式会社ビーブレイクシステムズ開発部所属。
社内サークル執筆チーム在籍。
主な執筆。
@IT連載『Javaの常識を変えるPlay framework入門』
日経ソフトウェア連載『コツコツ学ぶAndroidネイティブアプリ開発教室』
JavaOne Tokyo 2012まとめレポート(後編):ラムダ式、JAR脱獄、JavaScript/■■Node.jsに接近するJDK 8、そして9へ
スケーラブルで関数型でオブジェクト指向なScala入門(4):基本的なパターンマッチとScalaで重要な“関数”
WebSocketやREST APIのサポート強化:Java 8&Java EE 7に対応した「Spring Framework 4.0」正式版リリース
JDK 8、TLS 1.2がデフォルトに
Gaucheでメタプログラミング(1):ちょっと変わったLisp入門Copyright © ITmedia, Inc. All Rights Reserved.