概要
ラムダ式を扱うのに欠かせない java.util.function (Java Platform SE 8) 内の関数型インターフェースについてまとめます。
個人的には基本となる4つの関数型インターフェースとそのサブクラス、引数を2つ受け取るように特殊化されたものだけ覚えておけばいいと思っています。
プリミティブ特殊化されたものは、数も多いのでそんな物あるんだ程度に頭の片隅に残しておき、使いたくなったら調べながら使えばいいと思っています。
沢山あるので似たような役割ごとにまとめてみました。
基本となる4つの関数型インターフェース
Function<T, R>
T 型の引数を受け取って、R 型の値を返すインターフェースです。
抽象メソッドは R apply (T t) です。
static メソッドには入力値をそのまま出力する identity( ) があります。
インスタンス・メソッドには合成関数を作成する andThen( ) と compose( ) があります。
function1.andThen( function2 ) は function1 の後に引数に指定した function2 を実行します。
function1.compose( function2 ) は引数に指定した function2 を function1 より先に実行します。
Consumer<T>
T 型の引数を受け取って、何も値を返さない「消費者」を意味するインターフェースです。
抽象メソッドは void accept (T t) です。
static メソッドはありません。
インスタンス・メソッドに Function 同様に合成関数を作成する andThen( ) があります。
consumer1.andThen( consumer2 ) は consumer1 の後に引数に指定した consumer2 を実行します。
Supplier<T>
引数を何も受け取らず、T 型の値を返す「供給者」を意味するインターフェースです。
抽象メソッドは T get ( ) です。
その他に、static メソッドもインスタンス・メソッドも用意されていません。
Predicate<T>
T 型の引数を受け取って boolean 型の値を返す「断定」を意味するインターフェースです。
抽象メソッドは boolean test (T t) です。
static メソッドには値を比較する isEquals( ) があります。
インスタンス・メソッドには論理積を取る and( ) 論理和を取る or( ) 論理否定を取る negate( ) があります。
サンプルコード
基本となる 4 つの関数型インターフェースの抽象メソッドの使い方を示します。
関数型インターフェース | 抽象メソッド |
---|---|
Function<T, R> | R apply (T t) |
Consumer<T> | void accept (T t) |
Supplier<T> |
T get ( ) |
Predicate<T> |
boolean test (T t) |
import java.util.function.*;
public class Sample {
public static void main(String[] args) {
String message = "Hello";
// Function<T, R>
Function<String, Integer> function = str -> str.length();
System.out.println(function.apply(message)); // 5
// Consumer<T>
Consumer consumer = str -> System.out.println(str);
consumer.accept(message); // Hello
// Supplier<T>
Supplier supplier = () -> message + " World";
System.out.println(supplier.get()); // Hello World
// Predicate<T>
Predicate predicate = str -> str.length() > 5;
System.out.println(predicate.test(message)); // false
}
}
関数型インターフェースの特殊化
引数を2つ受け取る
基本となる関数型インターフェースの引数を2つ受け取るように特殊化されたものも存在します。
なお、Supplier<T> は引数を何も取らないインターフェースのため、引数を2つ取るように特殊化されたものは存在しません。
-
BiFunction<T, U, R>
抽象メソッドは R apply (T t, U u) -
BiConsumer<T, U>
抽象メソッドは void accept (T t, U u) -
BiPredicate<T, U>
抽象メソッドは boolean test (T t, U u)
関数型インターフェースのサブクラス
基本となる関数型インターフェースを継承したインターフェースも存在します。
ただし、Function<T, R> か BiFunction<T, U, R> がベースとなっており、Consumer<T>, Supplier<T>, Predicate<T> を継承したインターフェースは存在しません。
UnaryOperation<T>
Function インターフェースのサブクラスです。
public interface UnaryOperation<T> extends Function<T, T>
extends 部分を見ると、前述した Function<T, R> が Function<T, T> となっています。
これは、引数で受け取った型と同じ型の値を返すと言う意味になります。
抽象メソッドは T apply (T t) です。
Function インターフェースを継承しているので andThen( ), compose( ), identity( ) も同様に使えます。
BinaryOperator<T>
BiFunction インターフェースのサブクラスです。
public interface BinaryOperator<T> extends BiFunction<T, T, T>
extends 部分を見ると、前述した BiFunction<T, U, R> が BiFunction<T, T, T> となっています。
これは、引数2つの型と返り値の型が全てが同じ型と言う意味になります。
抽象メソッドは T apply (T t, T t) です。
BiFunction インターフェースを継承しているので andThen( ) も同様に使えます。
static メソッドで引数で受け取った2つの値のうち、小さい方を返す minBy( ) と大きい方を返す maxBy( ) もあります。
関数型インターフェースのプリミティブ特殊化
Int, Long, Double のプリミティブ型の引数を受け取るように特殊化されたものも存在します。
Supplier<T> は引数を何も受け取らないインターフェースのため、プリミティブ型の引数を受け取るように特殊化されたものは存在しません。
Int 型の引数を受け取る
- IntFunction<R>
抽象メソッドは R apply (int value)
返り値の型 R は指定できます - IntConsumer
抽象メソッドは void accept (int value) - IntPredicate
抽象メソッドは boolean test (int value)
Long 型の引数を受け取る
- LongFunction<R>
抽象メソッドは R apply (long value)
返り値の型 R は指定できます - LongConsumer
抽象メソッドは void accept (long value) - LongPredicate
抽象メソッドは boolean test (long value)
Double 型の引数を受け取る
- DoubleFunction<R>
抽象メソッドは R apply (double value)
返り値の型 R は指定できます - DoubleConsumer
抽象メソッドは void accept (double value) - DoublePredicate
抽象メソッドは boolean test (double value)
Supplier<T> は返り値のプリミティブ型を指定
Supplier<T> は引数でなく、返り値がプリミティブ型を返すように特殊化されたものが存在します。
抽象メソッドは get ( ) ではなく、getAsXXX ( ) となっています。
XXX は返り値の型となるプリミティブ型を表しています。
- IntSupplier
抽象メソッドは int getAsInt( ) - LongSupplier
抽象メソッドは long getAsLong ( ) - DoubleSupplier
抽象メソッドは double getAsDouble ( ) - BooleanSupplier
抽象メソッドは boolean getAsBoolean ( )
Function<T> で返り値のプリミティブ型を指定
Supplier<T> 以外にも返り値のプリミティブ型を指定したインターフェースがあります。
Supplier<T> 以外と言いましたが、Consumer<T> は返り値の無いインターフェースですし、Predicate<T> は返り値が Boolean 型と決まっています。
なので、実質 Function<T, R> とBiFunction<T, U, R> が対象となります。
T 型の引数や U 型の引数を受け取って決められたプリミティブ型の値を返すので、インターフェース名が ToInt 〜、ToLong〜、ToDouble〜 と言う名前になっています。
- ToIntFunction<T>
抽象メソッドは int applyAsInt (T t) - ToLongFunction<T>
抽象メソッドは int applyAsLong (T t) - ToDoubleFunction<T>
抽象メソッドは int applyAsDouble (T t) - ToIntBiFunction<T, U>
抽象メソッドは int applyAsInt (T t, U u) - ToLongBiFunction<T, U>
抽象メソッドは int applyAsLong (T t, U u) - ToDoubleBiFunction<T, U>
抽象メソッドは int applyAsDouble (T t, U u)
更に Function<T, R> には引数も返り値も指定されたプリミティブ型を返すものが用意されています。
こちらは BiFunction<T, U, R> 用は無いです。
- IntToLongFunction
抽象メソッドは long applyAsLong (int value) - IntToDoubleFunction
抽象メソッドは double applyAsDouble (int value) - LongToIntFunction
抽象メソッドは int applyAsInt (long value) - LongToDoubleFunction
抽象メソッドは double applyAsDouble (long value) - DoubleToIntFunction
抽象メソッドは int applyAsInt (double value) - DoubleToLongFunction
抽象メソッドは long applyAsLong (double value)
Operator<T> は引数と返り値のプリミティブ型が一緒
Operator は引数と返り値が同じ型なので、Function<T, R> や BiFunction<T, U, R> と違って名前に To がつきません。
- IntUnaryOperator
抽象メソッドは int applyAsInt (int value) - LongUnaryOperator
抽象メソッドは long applyAsInt (long value) - DoubleUnaryOperator
抽象メソッドは double applyAsInt (double value) - IntBinaryOperator
抽象メソッドは int applyAsInt (int value1, int value2) - LongBinaryOperator
抽象メソッドは long applyAsLong (long value1, long value2) - DoubleBinaryOperator
抽象メソッドは double applyAsInt (double value1, double value2)
Consumer<T> だけ Object を引数で受け取る
Consumer<T> だけObject を引数で受け取ったインターフェースが存在します。
- ObjIntConsumer<T>
抽象メソッドは void accept (T t, int value) - ObjLongConsumer<T>
抽象メソッドは void accept(T t, long value) - ObjDoubleConsumer<T>
抽象メソッドは void accept(T t, long double)
まとめ
基本となる4つの関数型インターフェースとそのサブクラス、特殊化されたクラスについてまとめました。
関数型インターフェースには名前に Bi がついたり、To がついたり、メソッド名に As がついたりと規則性が見られます。
詳しい使い方やサンプルコードは少ないですが、java.util.function (Java Platform SE 8) 内の関数型インターフェースを似たようなグループでまとめたので、少しは見やすくなったかなと思います。