33.
ラムダ式
無名クラス
関数型インターフェイス
無名クラスは、インターフェースの実装のコーディンングを省略したクラスです。関数型インターフェイスともいい、インターフェイスのインスタンスを生成と同時に、ブロックスコープでメソッドを実装できます。
Runnableの無名クラス
「Runnable」を無名クラスで定義して、スレッドを実行します。
ファイル構成
lambda/
└── App1.java
Runnableの実行
「Runnable」インスタンスを生成し、メソッド実装します。
package lambda;
public class App1 {
public static void main(String[] args) {
Runnable runner = new Runnable() {
@Override
public void run() {
System.out.println("Hello!");
}
};
runner.run();
}
}
カスタムインターフェイス
インターフェイスを作成して、無名クラスで実行するパターンです。
ファイル構成
lambda/
├── CalculateApp.java
└── ICalculate.java
インターフェイス作成
ICalculateインターフェイスを作成し、calculateWithoutTaxPrice() を定義します。
ICalculate.java
package lambda;
public interface ICalculate {
public Integer calculateWithoutTaxPrice(Integer price);
}
メインアプリ作成
メインアプリCalculateAppを作成し、メインメソッドにICalculateインスタンスを生成します。
CalculateApp.java
package lambda;
class CalculateApp {
public static void main(String[] args) {
ICalculate calculate = new ICalculate() {
};
}
}
メソッドの実装
ICalculateインターフェイスにcalculateWithoutTaxPrice() の実装されていないため、コンパイルエラーになります。
合計金額の計算
クイックフィックスでcalculateWithoutTaxPrice() を自動挿入し、税抜き価格の計算処理を実装します。
CalculateApp.java
public static void main(String[] args) {
ICalculate calculate = new ICalculate() {
@Override
public Integer calculateWithoutTaxPrice(Integer price) {
Float withoutTaxPrice = price / 1.1f;
return withoutTaxPrice.intValue();
}
};
}
メソッドの実行
calculateWithoutTaxPrice() を実行して確認してみましょう。
CalculateApp.java
public static void main(String[] args) {
ICalculate calculate = new ICalculate() {
@Override
public Integer calculateWithoutTaxPrice(Integer price) {
Float withoutTaxPrice = price / 1.1f;
return withoutTaxPrice.intValue();
}
};
Integer withoutTaxPrice = calculate.calculateWithoutTaxPrice(1000);
System.out.println(withoutTaxPrice);
}
結果
909
ラムダ式とは
Javaのクロージャー
ラムダ式はメソッドをシンプルなコーディングする構文で、JavaSE 8から導入されました。ラムダ式はクロージャ(Closure) ともいい、スクリプト言語でよく利用される無名関数やアロー関数のように、メソッド名や引数を省略してコーディングできます。
ラムダ式の基本
1行で処理
Javaのラムダ式は、メソッドを「 ()-> 」で省略して記述します。以下は1行で処理する場合です。
()-> 処理;
複数行で処理
複数行で処理する場合は、ブロックスコープ「 { } 」で実装します。
()-> {
処理1;
処理2;
};
コールバック
メソッドによっては、引数にメソッドを定義することもできます。
メソッド(()-> 処理);
Runnableのラムダ式
インターフェイスのラムダ式
関数型インターフェイスでメソッドを実装すると複雑になるため、ラムダ式が用意されています。
1行の処理
インターフェース名 インスタンス = (引数)-> 処理;
複数行の処理
インターフェース名 インスタンス = (引数)-> {
処理1;
処理2;
};
インターフェイスメソッドの実行
インスタンス.メソッド();
Runnableのラムダ式
「Runnable」インターフェイスを、ラムダ式で実行してみます。
ファイル構成
lambda/
└── RunableApp.java
メインアプリ作成
lambdaパッケージにメインアプリRunableAppを作成します。
RunableApp.java
package lambda;
class RunableApp {
public static void main(String[] args) {
}
}
メソッドの実装
メインメソッドに「Runnable」インターフェイスのインスタンスを定義し、ラムダ式でメソッドを実装します。
RunableApp.java
public static void main(String[] args) {
Runnable runner = () -> System.out.println("Hello!");
}
スレッドの実行
run() でスレッドを実行します。
RunableApp.java
public static void main(String[] args) {
Runnable runner = () -> System.out.println("Hello!");
runner.run();
}
カスタムメソッドの定義
スレッド実行メソッド hello() と、文字列表示メソッド showMessage() メソッドを定義します。
RunableApp.java
package lambda;
class RunableApp {
...
public static void showMessage(String message) {
System.out.println(message);
}
public static void hello(Runnable r) {
r.run();
}
}
ラムダ式で実行
showMessage() を、スレッドで実行します。
AppRun.java
package lambda;
class AppRun {
public static void main(String[] args) {
...
hello(()-> showMessage("Hello!"));
}
public static void hello(Runnable runner) {
runner.run();
}
}
カスタムインターフェイスのラムダ式
カスタムインターフェイスをラムダ式で実行します。
ファイル構成
lambda/
├── CalculateApp.java
└── ICalculate.java
アロー関数で記述
「App2」クラスのプログラムをラムダ式で記述すると以下のようになります。
package lambda;
class CalculateApp {
public static void main(String[] args) {
ICalculate calculate = (price) -> {
Float withoutTaxPrice = price / 1.1f;
return withoutTaxPrice.intValue();
};
Integer price = calculate.calculateWithoutTaxPrice(1000);
System.out.println(price);
}
}
- インスタンス生成の省略
- 戻り値や引数のデータ型が省略
- メソッド名がないアロー関数:「 -> 」が矢印(Arrow)
結果
909
forEach
forEach() は for文(foreach)のラムダ式で、以下の構文になります。
1行の処理
ArrayList.forEach(変数 -> 処理);
複数行の処理
ArrayList.forEach(変数 -> {
処理1;
処理2;
});
forEachを使う
ファイル構成
lambda/
├── Drink.java
└── DrinkApp.java
通常のfor文
ArrayListをfor文(foreach)で繰り返すと以下のプログラムになります。
DrinkApp.java
public class DrinkApp {
public static void main(String[] args) {
ArrayList<Drink> drinkList = new ArrayList<>();
drinkList.add(new Drink("コーヒー", 350));
drinkList.add(new Drink("紅茶", 400));
drinkList.add(new Drink("ほうじ茶", 300));
for (Drink drink : drinkList) {
System.out.println(drink.name);
}
}
forEachで処理
forEachで処理すると以下のようになります。
DrinkApp.java
public class DrinkApp {
public static void main(String[] args) {
ArrayList<Drink> drinkList = new ArrayList<>();
drinkList.add(new Drink("コーヒー", 350));
drinkList.add(new Drink("紅茶", 400));
drinkList.add(new Drink("ほうじ茶", 300));
drinkList.forEach(drink -> {
System.out.println(drink.name);
System.out.println(drink.price);
});
}
演習
問題1
Javaのラムダ式の記述で正しいのはどれですか?
- () => 処理;
- () -> 処理;
- function() => 処理;
- function() -> 処理;
問題2
Javaのラムダ式の記述で正しいのはどれですか?
- () => { 処理; };
- () -> { 処理; };
- {} => ( 処理; );
- {} -> ( 処理; );
問題3
ArrayList
1)
cityList.forEach(city -> System.out.println(city));
2)
cityList.for(city -> System.out.println(city));
3)
cityList.forEach(() -> System.out.println(city));
4)
cityList.for(() -> System.out.println(city));
問題4
「drinkMap」をラムダ式で繰り返し、「Drink.name」を表示してみましょう。
package lambda;
import java.util.HashMap;
import java.util.Map;
import cafe.Drink;
public class AppDrinkMap {
public static void main(String[] args) {
Map<String, Drink> drinkMap = new HashMap<>();
drinkMap.put("D0001", new Drink("コーヒー", 350));
drinkMap.put("D0002", new Drink("紅茶", 400));
drinkMap.put("D0003", new Drink("ほうじ茶", 300));
//ラムダ式でDrinkの「name」を表示
}
}