動機
Java8でlambdaとかStreamとか追加クラスとかあるけど、Function, Consumer, Supplier, Predicateなどが基礎になっている気がするのでちゃんと理解する。
Function
使い方
Javaにおけるメソッドの挙動をインスタンスで扱う仕組み。
従来、以下のようにしていたメソッド定義を、
R functionName(T arg) {
return new R();
}
以下のような形で、インスタンス定義に変えることが出来る。
Function<T, R> functionalInstance = new Function<T, R>() {
@Override
public R apply(T arg) {
return new R();
}
};
これだと面倒なので、lambda式を使って以下でも良い。
Function<T, R> functionalInstance = arg -> new R();
メソッド定義の場合だと、以下のようにして呼び出ししていたが、
R returnValue = functionName((T) argument);
Function
を使った場合は、applyメソッドを使って、
R returnValue = functionalInstance.apply((T) argument);
とする。
Javaにおけるメソッドは第一級オブジェクトではないけれど、そういうスタイルを導入しやすいように新しく仕組みを作ったという感じだろうか。
Consumer
Functionが分かればあとは簡単で、Consumerは引数を消費するインスタンスを定義するためのインターフェイス。
引数は取るが返り値は存在しないので、ジェネリクスによる型指定は引数側の一つだけ。
イメージとしては、以下のメソッドのインスタンス版。
void consumer(T arg) {
something(arg);
}
定義方法は、以下の通り。acceptというメソッドをオーバーライドして挙動を定める。
Consumer<T> consumer = new Consumer<T>() {
@Override
public void accept(T arg) {
something(arg);
}
lambdaなら、以下。
Consumer<T> consumer = arg -> something(arg);
呼び出しは、以下のように実施する。
consumer.accept((T) argument)
Supplier
Supplierは、引数を取らないが返り値は存在する挙動を定義するためのインターフェイス。
イメージとしては、以下のメソッドのインスタンス版作るためのもの。
T consumer() {
return new T();
}
以下のように定義する。
Supplier<T> supplier = new Supplier<T>() {
@Override
public T get() {
return new T();
}
}
lambdaなら以下。
Supplier<T> supplier = () -> new T();
使用する場合は以下のようにする。
supplier.get();
Predicate
Predicateは、ある引数を受け取って、真偽値を返すインターフェース。
// 引数が1だったらtrue、それ以外はfalse
Predicate<Integer> predicate = arg -> arg == 1;
Integer argument = 1;
if (predicate.test(argument)) {
System.out.println("argument is 1");
} else {
System.out.println("argument is not 1");
}
という感じで使う。
Function<T, Boolean>
に近いと思うが、この場合だとBooleanからbooleanへのアンボクシングが入るので、それを避けるのが一つの目的だろうか。
andとかorというメソッドもあるようだが、あまり使わなさそうなので省略する。
synchronizedについて完全理解したいなら
synchronizedの解説と典型的誤り例
を書いたので、こちらもどうぞ。経験的に、7割くらいの人がなにか間違っています。