本連載では、今までJavaの経験はあっても「ラムダ式は、まだ知らない」という人を対象にラムダ式について解説していきます。今回は、基本文法の概要とさまざまな省略の仕方、匿名クラスとラムダ式の共通点と違い、応用的な使い方についてコード例を交えて解説します。
ついにJava SE 8、つまりJDK 8が正式リリースとなりました(日本時間3月19日、早朝)。本連載「Java 8はラムダ式でここまで変わる」では、今までJavaの経験はあっても「ラムダ式は、まだ知らない」という人を対象にラムダ式や、それに関連するJava 8の新しいAPIについて解説していきますので、正式リリースされたJava SE 8を使いこなせるように、ぜひご愛読ください。
前回の「初心者のためのJavaラムダ式入門とJDKのインストール、IDEの環境構築」ではJavaでのラムダ式の概要と利点、必要性、JDK 8のセットアップ、NetBeans、IntelliJ IDEA、Eclipseのe(fx)clipseプラグインの環境構築について解説しました。実行できる準備ができたところで今回は、ラムダ式の読み書きができるように基本的な文法を見ていきましょう。
前回のおさらいになりますが、ラムダ式は関数型インターフェースのメソッドに対して、下記の書式で実装を行います。
( 実装するメソッドの引数 ) -> { 処理 }
実際に実装されたものでないと分かりづらいと思うので、簡単なサンプルからラムダ式の書式を見てみましょう。このサンプルは実行すると「Hello, テスト 太郎」と表示するものです。
public static void main(String[] args) { SampleInterface sampleInterface = (String name) -> { System.out.println("Hello, " + name); }; sampleInterface.say("テスト 太郎"); } @FunctionalInterface private interface SampleInterface { public void say(String name); }
ここでは「SampleInterface」の「say」メソッドの実装を行うため左辺に引数として「name」を定義しています。今回は関数型インターフェースで宣言している引数と名前を同じにしましたが、違う名前(例えば「n」)でも問題ありません。
次に、先ほどの引数と実装する処理の間に「->」を記述しています。
最後に処理の実装を下記で行っています。ここのnameは左辺で宣言した引数「name」になります。
System.out.println("Hello, " + name)
これを「匿名クラス」の場合で見てみましょう。mainメソッドが下記のようになります。
public static void main(String[] args){ SampleInterface sampleInterface = new SampleInterface() { @Override public void say(String name ) { System.out.println("Hello, " + name); } }; sampleInterface.say("テスト 太郎"); }
5行目の「name」はラムダ式だと左辺の引数です。6行目は、ラムダ式だと右辺のターゲット式です。
以上が、ラムダ式の基本的な文法です。
ここからはラムダ式のさまざまな省略について見ていきましょう。
まず、例として対象の文字列が数値の場合はTrueを返す次の関数型インターフェースがあったとします。
@FunctionalInterface public interface IsNumberInterface { boolean check(String value); }
この関数型インターフェースのメソッドは対象の文字列が数値かを判別するものです。このメソッドを実行すると、対象の文字列が数値に変換できる場合はTrueを、数値に変換できない場合はFalseを返すように実装します。
ラムダ式では記述をシンプルにするために、いろいろなものが省略できますが、まずは何も省略しなかった場合を見てみましょう。
IsNumberInterface isNumberInterface = (String value) -> { try { new BigDecimal(value); return true; } catch (Exception e) { return false; } };
ここではラムダ式の左辺に引数の型を記述していますが、この引数の型は次のように省略可能です。
IsNumberInterface isNumberInterface = (value) -> { ……
これはインターフェースの宣言時に引数の型を指定しているため、実装されるべき引数の型は指定がなくても判断できるためです。この型が特定できる箇所での型の省略は「型推論」と呼ばれるもので、Java 8ではラムダ式で使いやすいように型推論が使えるようになっています。
また、引数が一つの場合は丸括弧「()」を省略することも可能です。
IsNumberInterface isNumberInterface = value -> { ……
ただし、もし実装するメソッドに引数がない場合や複数の引数がある場合は、丸括弧「()」を省略することはできません。
ちなみに、メソッドに引数がない場合は次のように記述します。
() -> { ……
ラムダ式では処理の記述であるターゲット式が1つの文のみで表現できる場合、波括弧「{}」を外して記述することも可能です。そして、この場合は戻り値の有無にかかわらず「return」は記述しません。
例えば、次のような何らかの計算を行う関数型インターフェースがあったとします。
@FunctionalInterface public interface CalcInterface { BigDecimal calc(BigDecimal value1, BigDecimal value2); }
この関数型インターフェースをNullの確認など考えず単純な足し算で実装した場合、次のように記述できます。
CalcInterface calcInterface = (value1, value2) -> { return value1.add(value2); }
この場合、ターゲット式を1文で表現できるので、波括弧「{}」は省略できます。
CalcInterface calcInterface = (value1, value2) -> value1.add(value2);
また、三項演算子も1文として表現できるので波括弧「{}」を省略できます。
例えば先ほどの処理では引数がNullかどうかの確認をしていませんが、今回は三項演算子を使ってNullかどうかのチェックを入れてみます。このチェックの結果、引数がNullでない場合は加算を、どちらかがNullの場合は0(ゼロ)を返すようにした場合、次のようになります。
CalcInterface calcInterface = (value1, value2) -> (value1 != null && value2 != null) ? value1.add(value2) : BigDecimal.ZERO;
ちなみにJavaの三項演算子では条件後の処理がvoidで戻り値がない場合、式を評価できずにコンパイルエラーになります。次のような場合はコンパイルエラーになり三項演算子を使えません。
// 下記はエラーになります。 SampleInterface sampleInterface = name -> (name != null) ? System.out.println("Hello, " + name) : System.out.println("name is null");
ラムダ式の文法にも制限事項があります。その中の一つがラムダ式の引数にアンダーバー「_」のみのものは使えないということです。次の場合は、コンパイル・エラーになります。
( _ ) -> 処理
ただし、アンダーバー以降に文字があるもの(例えば「_value」)は使えます。
Copyright © ITmedia, Inc. All Rights Reserved.