java-recipes

ホーム 関数型プログラミング › Func-01

Func-01: java.util.function パッケージの関数型インターフェース

Java 8 で導入された java.util.function パッケージには、 関数をオブジェクトとして扱うための「関数型インターフェース」が揃っています。 Stream API やラムダ式と組み合わせて使うことで、簡潔で読みやすいコードが書けます。

4種類の基本インターフェース

java.util.function パッケージには 40種類以上のインターフェースがありますが、基本となるのは以下の4種類です。 これらを組み合わせることで、ほとんどのユースケースに対応できます。

インターフェース型シグネチャ呼び出しメソッド主な用途
Function<T, R>T → Rapply(T t)型変換・マッピング
Consumer<T>T → voidaccept(T t)ログ出力・画面表示・副作用
Supplier<T>() → Tget()遅延生成・ファクトリ
Predicate<T>T → booleantest(T t)フィルタリング・条件判定

関数合成のデフォルトメソッド

  • Function.andThen(f): 元の関数を適用した後に f を適用します(「AをしてからBをする」)
  • Function.compose(f): f を先に適用してから元の関数を適用します(andThen の逆順)
  • Predicate.and(p): 両方の条件が true のとき true を返します
  • Predicate.or(p): どちらか一方が true のとき true を返します
  • Predicate.negate(): 条件を反転します(true ↔ false)

サンプルコード

FunctionInterfaceSample.java
import java.util.Arrays;
import java.util.List;
import java.util.function.*;

public class FunctionInterfaceSample {

    public static void main(String[] args) {
        System.out.println("=== Function<T, R>: T → R の変換 ===");
        Function<String, Integer> strLength = s -> s.length();
        Function<Integer, String> intToStr = n -> "数値: " + n;

        System.out.println(strLength.apply("Hello")); // 5
        System.out.println(intToStr.apply(42));        // 数値: 42

        // andThen で関数合成
        Function<String, String> combined = strLength.andThen(intToStr);
        System.out.println(combined.apply("Java")); // 数値: 4

        System.out.println("\n=== Consumer<T>: T → void(副作用のみ) ===");
        Consumer<String> printer = s -> System.out.println("[ログ] " + s);
        printer.accept("処理を開始しました");

        List<String> items = Arrays.asList("A", "B", "C");
        items.forEach(printer); // Consumer を forEach に渡す

        System.out.println("\n=== Supplier<T>: 引数なし → T を返す ===");
        Supplier<String> greeting = () -> "こんにちは!";
        System.out.println(greeting.get()); // こんにちは!

        // 遅延評価(呼び出すまで生成されない)
        Supplier<List<String>> listFactory = () -> Arrays.asList("x", "y", "z");
        System.out.println(listFactory.get());

        System.out.println("\n=== Predicate<T>: T → boolean(フィルタリング) ===");
        Predicate<Integer> isPositive = n -> n > 0;
        Predicate<Integer> isEven = n -> n % 2 == 0;
        Predicate<Integer> isPositiveAndEven = isPositive.and(isEven);

        System.out.println("5 は正の偶数: " + isPositiveAndEven.test(5));  // false
        System.out.println("4 は正の偶数: " + isPositiveAndEven.test(4));  // true
        System.out.println("-2 は正の偶数: " + isPositiveAndEven.test(-2)); // false

        System.out.println("\n=== BiFunction<T, U, R>: 2引数版 ===");
        BiFunction<String, Integer, String> repeat = (s, n) -> {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < n; i++) sb.append(s);
            return sb.toString();
        };
        System.out.println(repeat.apply("ab", 3)); // ababab

        System.out.println("\n=== UnaryOperator<T>: T → T(同じ型の変換) ===");
        UnaryOperator<String> toUpperCase = String::toUpperCase;
        System.out.println(toUpperCase.apply("hello")); // HELLO

        System.out.println("\n=== BinaryOperator<T>: (T, T) → T ===");
        BinaryOperator<Integer> add = (a, b) -> a + b;
        System.out.println(add.apply(3, 4)); // 7
    }
}

Java 8 で java.util.function パッケージが導入されました。Function・Consumer・Supplier・Predicate の4種類が基本です。andThen() や and()/or() などのデフォルトメソッドで関数を合成できます。

よくあるミス・注意点

⚠️ Consumer で戻り値を期待してしまう(戻り値は void)

Consumer<T>accept()void を返します。 「処理した結果を受け取りたい」場合は Function<T, R> を使ってください。 Consumer はログ出力や画面表示など「値を渡して何かをするが結果は不要」なときに使います。

⚠️ Predicate.and() と && の違い(ショートサーキット評価)

Predicate.and()&& と同様にショートサーキット評価(左辺が false なら右辺を評価しない)を行います。 ただし、and() を使うと Predicate をオブジェクトとして渡したり組み合わせたりできるため、 Stream の filter() に直接渡すときに便利です。 一方、ラムダ式の中に直接 && を書くほうが読みやすいこともあります。 使い分けのポイントは「Predicate を再利用・合成したいかどうか」です。

テストする観点

  • Function.andThen() で合成した関数が、単独適用を順番に行った結果と一致すること
  • Predicate.and() の結果が、手書きの && と同じ真偽値を返すこと(境界値: どちらかが false のとき)
  • Predicate.or() の結果が、|| と同じ真偽値を返すこと
  • Supplierget() が呼ばれるたびに新しいインスタンスを返すこと(遅延評価の確認)
  • BiFunction が2引数を受け取り期待通りの結果を返すこと

GitHub でソースコードを見る →