無名クラス

関数型インターフェイス

無名クラスは、インターフェースの実装のコーディンングを省略したクラスです。関数型インターフェイスともいい、インターフェイスのインスタンスを生成と同時に、ブロックスコープでメソッドを実装できます。

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文

ArrayListfor文(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のラムダ式の記述で正しいのはどれですか?

  1. () => 処理;
  2. () -> 処理;
  3. function() => 処理;
  4. function() -> 処理;

問題2

Javaのラムダ式の記述で正しいのはどれですか?

  1. () => { 処理; };
  2. () -> { 処理; };
  3. {} => ( 処理; );
  4. {} -> ( 処理; );

問題3

ArrayList の「cityList」をラムダ式で繰り返したのはどれですか?

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」を表示
    }
}