[Java] ラムダ式と関数型インターフェース

ラムダ式とは前述の匿名クラスの記述をより簡潔に書くことが出来る記述法で、Java8から導入されました。

また、それに合わせてラムダ式で記述できるいくつかの関数型インターフェースが用意されているので、主要な部分についてまとめていきます。

ラムダ式

構文

インターフェース 変数 = (引数) -> {処理};

定義例

例えば匿名クラスの説明で利用した次のサンプルコードがあります。

https://www.s-watanabe.work/2022/10/java.html#3

interface Itest {
    public void doTest();
}

public class AnonymousTest {
    public static void main(String[] args) {
        Itest itest = new Itest() {
            @Override
            public void doTest() {
                System.out.println("Itest.doTest");
            }
        };
        
        itest.doTest();
    }
}

このコードはラムダ式によって次のように置き換えることが出来ます。

interface Itest {
    public void doTest();
}

public class AnonymousTest {
    public static void main(String[] args) {
        Itest itest = () -> {
            System.out.print("Itest.doTest");
        };
        itest.doTest();
    }
}

処理内容が1行の場合、中カッコ{}を省略することができます。

Itest itest = () -> System.out.print("Itest.doTest");
  • ラムダ式は関数型インターフェースの要件を満たしているもののみ記述可能(抽象クラスや具象クラスはラムダ式で記述できない)。
  • 関数型インターフェースとは、定義されている抽象メソッドが1つだけのインタフェースのこと(staticメソッドやdefaultメソッドは定義されていても問題ない)。

定義例(引数を持つ場合)

interface Itest {
    public void doTest(String v);
}

public class AnonymousTest {
    public static void main(String[] args) {
        Itest itest = (String v) -> {
            System.out.println(v);
        };
        itest.doTest("LambdaTest.doTest");
    }
}

引数の型はJVMがインターフェースから推測するため省略できます。

Itest itest = (v) -> {
    System.out.println(v);
};

さらに引数が1つの場合、カッコ()も省略できます。

Itest itest = v -> {
    System.out.println(v);
};

関数型インターフェース

上述の通り関数型インターフェースとは、抽象メソッドを一つだけ持ったインターフェースを指し、ラムダ式に置き換えることによってコードをシンプルに実装することが出来ます。

Java8から、あらかじめいくつかの関数型インターフェースが用意されており、主要なものについて以下にまとめます。

Supplier

Supplierインターフェースの特徴は、引数を受け取らずに何らかの値を返却します。

インターフェース仕様

パッケージ java.util.function
インターフェース Supplier<T>
戻り値の型 T
抽象メソッド get()

定義例

import java.util.function.Supplier;

public class LambdaTest {
    public static void main(String[] args) {
        Supplier<String> supplier = () -> {
            return "Supplier.get";
        };

        System.out.println(supplier.get());
    }
}

Consumer

Consumerインターフェースの特徴は、引数を受け取り何らかの処理を実行します。戻り値は返しません。

インターフェース仕様

パッケージ java.util.function
インターフェース Consumer<T>
戻り値の型 void
抽象メソッド accept(T)

定義例

import java.util.function.Consumer;

public class LambdaTest {
    public static void main(String[] args) {
        Consumer<String> consumer = (String v) -> {
            System.out.println(v);
        };

        consumer.accept("Consumer.get");
    }
}

メソッド参照

上記のコードはメソッド参照により以下のように置き換えることが出来ます。

Consumer<String> consumer = System.out::println;

BiConsumer

Consumerインターフェースが引数を一つしか受け取らないのに対し、BiConsumerインターフェースは二つの引数を受け取り何らかの処理を実行します。

Predicate

Predicateインターフェースの特徴は、引数を受け取り何らかの判定を行い、結果をbooleanで返します。

インターフェース仕様

パッケージ java.util.function
インターフェース Predicate<T>
戻り値の型 boolean
抽象メソッド test(T)

定義例

import java.util.function.Predicate;

public class LambdaTest {
    public static void main(String[] args) {
        Predicate<Integer> p = (x) -> {
            return x < 10;
        };

        if (p.test(9)) {
            // Some code
        }
    }
}

BiPredicate

Predicateインターフェースが引数を一つしか受け取らないのに対し、BiConsumerインターフェースは二つの引数を受け取り何らかの判定を行います。

Function

Functionインターフェースの特徴は、引数を受け取り何らかの処理を行い、結果を任意の型で返します。

インターフェース仕様

パッケージ java.util.function
インターフェース Function<T, R>
Rは戻り値の型
戻り値の型 R
抽象メソッド apply(T)

定義例

import java.util.function.Function;

public class LambdaTest {
    public static void main(String[] args) {
        Function<Integer, Integer> f = (x) -> {
            return x * 10;
        };

        System.out.println(f.apply(10));
    }
}

処理内容が1行の場合、次のように置き換えることが可能です。

Function<Integer, Integer> f = (x) -> x * 10;

合成メソッド

FunctionインターフェースにはandThenとcomposeという合成メソッドが用意されています。Functionインターフェース同士を合成することで、複数の処理をつなぎ合わせて一つの処理として実行することが出来ます。

andThenとcomposeの違いは実行する順番だけで、andThenが指定準通り、composeが指定の逆順に処理を行います。

import java.util.function.Function;

public class LambdaTest {
    public static void main(String[] args) {
        Function<Integer, Integer> f1 = (x) -> x * 10;
        Function<Integer, Integer> f2 = (x) -> x + 5;

        System.out.println("andThen: (10 * 10) + 5 = " + f1.andThen(f2).apply(10));
        System.out.println("compose: (10 + 5) * 10 = " + f1.compose(f2).apply(10));
    }
}

このコードを実行すると次のように表示されます。

java LambdaTest.java
andThen: (10 * 10) + 5 = 105
compose: (10 + 5) * 10 = 150

BiFunction

Functionインターフェースが引数を一つしか受け取らないのに対し、BiFunctionインターフェースは二つの引数を受け取り何らかの処理を行い結果を返します。

コメント

このブログの人気の投稿

docker-compose up で proxyconnect tcp: dial tcp: lookup proxy.example.com: no such host

docker-compose で起動したweb、MySQLに接続できない事象

【PHP】PHP_CodeSnifferを使う(コーディングルールのカスタマイズ)