エンタープライズギークス (Enterprise Geeks)

企業システムの企画・開発に携わる技術者集団のブログです。開発言語やフレームワークなどアプリケーション開発に関する各種情報を発信しています。ウルシステムズのエンジニア有志が運営しています。

Functionインタフェースに定義されている関数の合成を試してみた

Java8のFunctionインターフェースに提供されている関数の合成を試してみた。提供されている関数はcomposeandThenの2つである。

関数の合成の処理はとても簡単だ。Java8のFunctionインターフェースの実装を見ると、処理はたった2行で実装されている。nullチェックが1行で、ラムダ式で書かれた本体が1行である。

以下は、Java8のソースコードからの引用である。

default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
    Objects.requireNonNull(before);
    return (V v) -> apply(before.apply(v));
}

default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
    Objects.requireNonNull(after);
    return (T t) -> after.apply(apply(t));
}

この関数の合成を試してみよう。
まずはFunctionインタフェースを使ったシンプルな関数(f1f2)を2つ定義して動かしてみる。

public class Main {
    public static void main(String[] args) {
        Function<Integer, Integer> f1 = x -> {
            System.out.print("f1 -> ");
            return 1 + x;
        };
        Function<Integer, Integer> f2 = x -> {
            System.out.print("f2 -> ");
            return 2 * x;
        };
        System.out.println("[f1]");
        System.out.println(f1.apply(0));

        System.out.println("[f2]");
        System.out.println(f2.apply(0));
    }
}

実行結果は以下のようになる。

[f1]
f1 -> 1
[f2]
f2 -> 0

続けて、composeandThenを使って関数を合成してみる。

public class Main {
    public static void main(String[] args) {
        // (以下は上のコードの続き)

        Function<Integer, Integer> f3 = f1.compose(f2);
        System.out.println("[f3: f1.compose(f2)]");
        System.out.println(f3.apply(0));

        Function<Integer, Integer> f4 = f1.andThen(f2);
        System.out.println("[f4: f1.andThen(f2)]");
        System.out.println(f4.apply(0));

        Function<Integer, Integer> f5 = f1.andThen(f2).compose(f1);
        System.out.println("[f5: f1.andThen(f2).compose(f1)]");
        System.out.println(f5.apply(0));
    }
}

実行すると、次のように関数が合成されることが分かる。

[f3: f1.compose(f2)]
f2 -> f1 -> 1
[f4: f1.andThen(f2)]
f1 -> f2 -> 2
[f5: f1.andThen(f2).compose(f1)]
f1 -> f1 -> f2 -> 4

ところで、このcomposeandThenによる関数合成は、Functionインターフェースに従っていないと合成できない。つまり、任意のFunctionalInterface同士を合成するための仕組みではない。
このため、FunctionalInterfaceを自作する際には、Functionインターフェースを実装してcomposeandThenによる関数合成を可能にするかどうかを意識しておく必要がある。

[近棟 稔]