拙者、あんまり正式リリースされてない API って扱わない保守的な人間なんですが、ふと Java8 で導入されるラムダ式ってのがどんな感じになるのか気になったのでドキュメントを探してみました。 ラムダ式に対応したドキュメントや JavaDoc は普通の「JDK〓 8 Early Access Releases」にはまだ含まれてないようで、「Java〓 Platform, Standard Edition 8 Early Access with Lambda Support」 からダウンロードが必要。
ちなみに、今回は java.util.function パッケージに含まれるインターフェースを見ていく記事で、実際にラムダ式を用いた関数オブジェクトの生成などコードは書いてません。 また、当然のことながら最終的に仕様が変更になってるかも知れませんのでご注意を。
参考
- 日々常々「Java8で実行時にメソッドの引数の名前がとれるぽい」
- Java.net 「Java〓 Platform, Standard Edition 8 Early Access with Lambda Support」
追記
ea-b120 で試してみたらチラホラと変更があったので所々修正しました(2014年1月1日)。- メソッドの追加・削除・変更
- Function に andThen(), identity() を追加
- BiFunction に andThen() を追加
- UnaryOperator に identity() を追加
- BinaryOperator に maxBy(), minBy() を追加
- Predicate に isEqual() を追加、xor() を削除
- BiPredicate から xor() を削除
- Consumer の chain() を andThen() に変更
- BiConsumer の chain() を andThen() に変更
- インターフェースの追加
- IntToLongFuncion
- IntToDoubleFunction
- LongToIntFuncion
- LongToDoubleFunction
- DoubleToIntFuncion
- DoubleToLongFunction
andThen() メソッドは Function, BiFunction, Consumer, BiConsumer に追加されてますが、これらは先に自分を作用させる合成を行う、という意味で統一されてるようです。 また、プリミティブ型を返り値に持つ関数インターフェースの抽象メソッドが applyAsInt() だったりするところを apply() と書いてたところを修正しました。
Overview
java.util.function パッケージに定義されているインターフェースを大雑把に分類するとこんな感じ:- Function / BiFunction ・・・ 関数
- UnaryOperator / BinaryOperator ・・・ 演算子
- Predicate / BiPredicate ・・・ 述語
- Supplier
- Consumer / BiConsumer
各インターフェースには抽象メソッドが1つずつ定義されています。 また、「Bi」が付いているのはその抽象メソッドの引数が「2つ」になってます。 引数、返り値がともに Object のもの(プリミティブ型でないもの)を表にまとめると以下の通り:
インターフェース | 抽象メソッド | 引数型 → 返り値型 |
Function BiFunction |
apply(...) | T → R T × U → R |
UnaryOperator BinaryOperator |
apply(...) | T → T T × T → T |
Predicate BiPredicate |
test(...) | T → boolean T × U → boolean |
Supplier |
get() | () → T |
Consumer BiConsumer |
accept(...) | T → () T × U → () |
Supplier は引数なし(よって BiSupplier というインターフェースはない)、Consumer は返り値が void です。 これらの他に、引数もしくは返り値がプリミティブ型のインターフェースがあれこれ定義されてますが、現在のところ全プリミティブ型には対応してなくて
- int
- long
- double
- boolean (Supplier のみ)
にのみ対応してるようです。
継承関係は Function 系と Operator 系との間にのみにあり*1、Operator は引数と返り値が同じ型の Function という関係になってます。 また、プリミティブ型が異なれば(int と long というように)やはり継承関係はありません。
java.util.function パッケージに定義されているクラスは Function 系, Predicate 系に対するユーティリティ・クラス Functions, Predicates があります。
では、それぞれのインターフェースをもう少し詳しく見ていきましょう。
Function / BiFunction (関数)
Function / BiFunction は関数を表すインターフェースです。 定義されている抽象メソッドは apply() です。Function (1引数)
- XxxFunction
- Function
- IntFunction
- LongFunction
- DoubleFunction
- ToXxxFunction
- ToIntFunction
- ToLongFunction
- ToDoubleFunction
- XxxToYyyFunction
- IntToLongFuncion
- IntToDoubleFunction
- LongToIntFuncion
- LongToDoubleFunction
- DoubleToIntFuncion
- DoubleToLongFunction
型宣言は以下の通り:
package java.util.function; @FunctionalInterface public interface Function<T,R>{ R apply(T t); default <V> Function<T,V> andThen(Function<? super R,? extends V> after); default <V> Function<V,R> compose(Function<? super V,? extends T> before); static <T> Function<T,T> identity() }
andThen(), compose() メソッドは関数の合成を行うためのメソッドです。 andThen() メソッドはこの Function オブジェクトを先に適用してその結果に引数の関数(Function オブジェクト)を作用させる、という Function オブジェクトを返します。 compose() メソッドはそれとは適用順序が逆の合成関数を返します。 identity() メソッド (static) は恒等関数(何もしない関数、引数をそのまま返す関数)に対応する Function オブジェクトを返します。
インターフェース | 抽象メソッド | 引数型 → 返り値型 |
Function<T, R> | R apply(T) | T → R |
IntFunction<R> LongFunction<R> DoubleFunction<R> |
apply(int) apply(long) apply(double) |
int → R long → R double → R |
ToIntFunction<T> ToLongFunction<T> ToDoubleFunction<T> |
applyAsInt(T) applyAsLong(T) applyAsDouble(T) |
T → int T → long T → double |
IntToLongFunction IntToDoubleFunction LongToIntFunction LongToDoubleFunction DoubleToIntFunction DoubleToLongFunction |
applyAsLong(int) applyAsDouble(int) applyAsInt(long) applyAsDouble(long) applyAsInt(double) applyAsLong(double) |
int → long int → double long → int long → double double → int double → long |
抽象メソッドが applyAsXxx() の場合、返り値が xxx 型だということに注意。 引数と返り値の型が同じプリミティブ型の関数を使いたい場合は、下記の XxxUnaryOperator を使って下さい。 Function インターフェース以外には andThen(), compose(), identity() メソッドはないようです。 プリミティブ型の関数にまで合成関数を返すメソッドを定義してたら、それこそ組み合わせ論的にメソッド数が爆発してしまいそうだし。 なんでまた、こんな仕様にしようとしたんだか。
BiFunction (2引数)
BiFunction は引数が2つの関数です。- BiFunction
- ToIntBiFunction
- ToLongBiFunction
- ToDoubleBiFunction
package java.util.function; @FunctionalInterface public interface BiFunction<T,U,R>{ R apply(T t, U u); default <V> BiFunction<T,U,V> andThen(Function<? super R,? extends V> after); }
andThen() メソッドは関数の合成を行うためのメソッドです。
インターフェース | 抽象メソッド | 引数型 → 返り値型 |
BiFunction<T, U, R> ToIntBiFunction<T, U> ToLongBiFunction<T, U> ToDoubleBiFunction<T, U> |
apply(T, U) applyAsInt(T, U) applyAsLong(T, U) applyAsDouble(T, U) |
T × U → R T × U → int T × U → long T × U → double |
UnaryOperator / BinaryOperator (演算子)
UnaryOperator は1項演算子*2、BinaryOperator は2項演算子*3です。 UnaryOperator は型パラメータが全て同じの Function、BinaryOperator は型パラメータが全て同じの BiFunction です。 抽象メソッドは Function / BiFunction と同じく apply() です。UnaryOperator (1引数)
- UnaryOperator
- IntUnaryOperator
- LongUnaryOperator
- DoubleUnaryOperator
package java.util.function; @FunctionalInterface public interface UnaryOperator<T> extends Function<T,T>{ static <T> UnaryOperator<T> identity(); }
identity() メソッド (static) は恒等演算(何もしない演算、引数をそのまま返す演算)を行う UnaryOperator オブジェクトを返します。 UnaryOperator は Function のサブタイプなので andThen(), compose() メソッドも使えます。
インターフェース | 抽象メソッド | 引数型 → 返り値型 |
UnaryOperator |
apply(T) | T → T |
IntUnaryOperator | applyAsInt(int) | int → int |
LongUnaryOperator | applyAsLong(long) | long → long |
DoubleUnaryOperator | applyAsDouble(double) | double → double |
BinaryOperator (2引数)
- BinaryOperator
- IntBinaryOperator
- LongBinaryOperator
- DoubleBinaryOperator
package java.util.function; @FunctionalInterface public interface BinaryOperator<T> extends BiFunction<T,T,T>{ static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator); static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator); }
maxBy(), minBy() メソッド (static) は、それぞれ、引数の2つの値のうち大きい方、小さい方を返す二項演算子を返します。 引数は比較を行う Comparator です。 BiFunction から継承した andThen() メソッドもあります。
BinaryOperator |
apply(T, T) | T × T → T |
IntBinaryOperator | applyAsInt(int, int) | int × int → int |
LongBinaryOperator | applyAsLong(long, long) | long × long → long |
DoubleBinaryOperator | applyAsDouble(double, double) | double × double → double |
Predicate / BiPredicate (述語)
Prediate / BiPredicate は述語を表すインターフェースです。 「述語」とは主語等にあたる変数の値が確定すれば真偽が確定するテストと言えばいいんでしょうかね。 気持ち的には「ToBooleanFunction / ToBooleanBiFunction」と言えます。 ただし、Function / BiFunction インターフェースとの関係(継承関係など)はありません。 定義されている抽象メソッドは test() です。Predicate (1引数)
- Predicate
- IntPredicate
- LongPredicate
- DoublePredicate
package java.util.function; @FunctionalInterface public interface Predicate<T>{ boolean test(T t); default Predicate<T> negate(); default Predicate<T> and(Predicate<? super T> p); default Predicate<T> or(Predicate<? super T> p); static <T> Predicate<T> isEqual(Object targetRef); }
negate(), and(), or() メソッドは、この Predicate オブジェクトと引数の Predicate オブジェクトの評価結果に対して論理演算を行う Predicate オブジェクトを生成します。 and(), or() はショートカット演算子 &&, || のように、不必要な評価は行われません。 isEqual() メソッド (static) は引数のオブジェクトと同じ(Object#equals() によって)オブジェクトに対して test() を呼ばれたときに true を返すような Predicate オブジェクトを返します。
インターフェース | 抽象メソッド | 引数型 → 返り値型 |
Predicate |
test(T) | T → boolean |
IntPredicate | test(int) | int → boolean |
LongPredicate | test(long) | long → boolean |
DoublePredicate | test(double) | double → boolean |
BiPredicate (2引数)
- BiPredicate
package java.util.function; @FunctionalInterface public interface BiPredicate<T,U>{ boolean test(T t, U u); default BiPredicate<T,U> negate(); default BiPredicate<T,U> and(BiPredicate<? super T,? super U> p); default BiPredicate<T,U> or(BiPredicate<? super T,? super U> p); }
negate(), and(), or() メソッドは、この BiPredicate オブジェクトと引数の BiPredicate オブジェクトの評価結果に対して論理演算を行う BiPredicate オブジェクトを生成します。 and(), or() はショートカット演算子 &&, || のように、不必要な評価は行われません。
インターフェース | 抽象メソッド | 引数型 → 返り値型 |
BiPredicate |
test(T, U) | T × U → boolean |
Supplier
Supplier は何らかの値を供給 (supply) するインターフェースです。 引数のない Function と思えばいいでしょう。 当然のことながら、2引数の BiSupplier なんてのはありません。 プリミティブ型のサポートとして、int, long, double 値以外にも、boolean 値を返す BooleanSupplier というのも定義されています。 抽象メソッドは get() です。- Supplier
- BooleanSupplier
- IntSupplier
- LongSupplier
- DoubleSupplier
package java.util.function; @FunctionalInterface public interface Supplier<T>{ T get(); }
インターフェース | 抽象メソッド | 引数型 → 返り値型 |
Supplier |
get() | () → T |
BooleanSupplier | getAsBoolean() | () → boolean |
IntSupplier | getAsInt() | () → int |
LongSupplier | getAsLong() | () → long |
DoubleSupplier | getAsDouble() | () → double |
Consumer / BiConsumer
Consumer インターフェースは値を受け取って処理をするが、返り値を返さないインターフェースです。 返り値のない(返り値が void の) Function と思えばいいでしょう。 ただし、返り値がないということはメソッド内でなんらかの副作用を伴う処理をしていることになるので、関数型プログラミングを行うときは注意が必要ってことにんるんでしょうね。 定義されている抽象メソッドは accept() です。Consumer (1引数)
- Consumer
- IntConsumer
- LongConsumer
- DoubleConsumer
package java.util.function; @FunctionalInterface public interface Consumer<T>{ void accept(T t); default Consumer<T> andThen(Consumer<? super T> after); }
chain() メソッドは、この Consumer オブジェクトと引数の Consumer オブジェクトの処理を連続して行う Consumer オブジェクトを生成します。 処理の順序はこの Consumer オブジェクトの方が先に行われます。
インターフェース | 抽象メソッド | 引数型 → 返り値型 |
Consumer |
accept(T) | T → () |
IntConsumer | accept(int) | int → () |
LongConsumer | accept(long) | long → () |
DoubleConsumer | accept(double) | double → () |
BiConsumer (2引数)
- BiConsumer
- ObjIntConsumer
- ObjLongConsumer
- ObjDoubleConsumer
package java.util.function; @FunctionalInterface public interface BiConsumer<T,U>{ void accept(T t, U u); default BiConsumer<T,U> andThen(BiConsumer<? super T,? super U> after); }
chain() メソッドは、この BiConsumer オブジェクトと引数の BiConsumer オブジェクトの処理を連続して行う BiConsumer オブジェクトを生成します。 処理の順序はこの BiConsumer オブジェクトの方が先に行われます。
インターフェース | 抽象メソッド | 引数型 → 返り値型 |
BiConsumer |
accept(T, U) | T × U → () |
ObjIntConsumer |
accept(T, int) | T × int → () |
ObjLongConsumer |
accept(T, long) | T × long → () |
ObjDoubleConsumer |
accept(T, double) | T × double → () |
@FunctionalInterface
上記の各インターフェースには共通するスーパーインターフェースはありませんが、関数型であることを表すマーカーインターフェース @FunctionalInterface アノテーションが付与されています。 定義はこんなの:package java.lang; @Documented @Retention(value=RUNTIME) @Target(value=TYPE) public @interface FunctionalInterface{}
ちなみに、サブタイプには継承されないようなので、(Function インターフェースのサブインターフェースである) UnaryOperator インターフェースなどにも付与されてます。
オブジェクト指向プログラマが次に読む本 ?Scalaで学ぶ関数脳入門
- 作者: 株式会社テクノロジックアート,長瀬嘉秀,町田修一
- 出版社/メーカー: 技術評論社
- 発売日: 2010/11/13
- メディア: 単行本(ソフトカバー)
- 購入: 11人 クリック: 340回
- この商品を含むブログ (32件) を見る
- 作者: 荒井省三:いげ太
- 出版社/メーカー: 技術評論社
- 発売日: 2011/01/07
- メディア: 大型本
- 購入: 6人 クリック: 264回
- この商品を含むブログ (26件) を見る