Java ラムダ式
関数型インターフェースとラムダ式を学びます。
ラムダ式とは
匿名関数を簡潔に書くための構文。
// 匿名クラス(従来)
Comparator<String> comparator = new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return s1.length() - s2.length();
}
};
// ラムダ式
Comparator<String> comparator = (s1, s2) -> s1.length() - s2.length();
基本構文
// 完全な形
(Type param1, Type param2) -> { statements; return value; }
// 型推論(型は省略可能)
(param1, param2) -> { statements; return value; }
// 単一式(波括弧とreturnを省略)
(param1, param2) -> expression
// 引数が1つ(括弧を省略)
param -> expression
// 引数なし
() -> expression
例
// 引数なし
Runnable r = () -> System.out.println("Hello");
// 引数1つ
Consumer<String> c = s -> System.out.println(s);
// 引数2つ
BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
// 複数文
Function<String, Integer> parse = s -> {
System.out.println("Parsing: " + s);
return Integer.parseInt(s);
};
関数型インターフェース
抽象メソッドを1つだけ持つインターフェース。@FunctionalInterface で明示。
標準の関数型インターフェース
| インターフェース | メソッド | 用途 |
|---|---|---|
Function<T, R> | R apply(T t) | 変換 |
Consumer<T> | void accept(T t) | 消費(副作用) |
Supplier<T> | T get() | 生成 |
Predicate<T> | boolean test(T t) | 判定 |
BiFunction<T, U, R> | R apply(T t, U u) | 2引数の変換 |
BiConsumer<T, U> | void accept(T t, U u) | 2引数の消費 |
BiPredicate<T, U> | boolean test(T t, U u) | 2引数の判定 |
UnaryOperator<T> | T apply(T t) | 同じ型への変換 |
BinaryOperator<T> | T apply(T t1, T t2) | 2引数の同じ型への変換 |
Function
Function<String, Integer> length = s -> s.length();
int len = length.apply("Hello"); // 5
// 合成
Function<String, String> upper = String::toUpperCase;
Function<String, Integer> upperThenLength = upper.andThen(length);
int result = upperThenLength.apply("hello"); // 5
// compose(逆順)
Function<Integer, String> intToString = Object::toString;
Function<Integer, Integer> stringLength = intToString.andThen(length);
Consumer
Consumer<String> print = s -> System.out.println(s);
print.accept("Hello");
// チェーン
Consumer<String> printUpper = s -> System.out.println(s.toUpperCase());
Consumer<String> printBoth = print.andThen(printUpper);
printBoth.accept("hello");
// hello
// HELLO
// Stream での使用
names.forEach(print);
Supplier
Supplier<String> supplier = () -> "Hello";
String value = supplier.get(); // "Hello"
// 遅延評価
Supplier<ExpensiveObject> lazy = () -> new ExpensiveObject();
// この時点ではインスタンス化されない
ExpensiveObject obj = lazy.get(); // ここで初めてインスタンス化
Predicate
Predicate<String> isEmpty = s -> s.isEmpty();
boolean result = isEmpty.test(""); // true
// 合成
Predicate<String> isNotEmpty = isEmpty.negate();
Predicate<String> isShort = s -> s.length() < 5;
Predicate<String> isShortAndNotEmpty = isNotEmpty.and(isShort);
// Stream での使用
List<String> nonEmpty = names.stream()
.filter(isNotEmpty)
.toList();
プリミティブ特化型
ボクシングを避けるための特化型。
IntFunction<String> intToString = i -> "Number: " + i;
IntConsumer printInt = System.out::println;
IntSupplier randomInt = () -> (int) (Math.random() * 100);
IntPredicate isEven = n -> n % 2 == 0;
IntUnaryOperator doubleIt = n -> n * 2;
IntBinaryOperator add = (a, b) -> a + b;
// ToIntFunction: 任意の型からintへ
ToIntFunction<String> length = String::length;
メソッド参照
ラムダ式をさらに簡潔に書く方法。