はじめに
Java SE 8 Goldを勉強していたときに、ややこしかったところなのでまとめ。
他のプリミティブ特殊型の関数型インタフェースについても触れている。
注)Gold受験者向けの内容
実は一言で解決する
この問題を解決するルールは、
『戻り値の型が一つに決められた関数型インタフェースは、該当メソッド=基本のメソッド+As+戻り値の型となる』
という一言に集約される。
java.util.function内の関数型インタフェースについて学習できる(私が勝手に作った)含蓄のある言葉なので、紐解いていこう。
java.util.functionで用意されている基本の関数型インタフェースとメソッドの対応
まずは基本中の基本。ここを覚えておかないとGold取得はなかなか難しい。逆にここが分かれば、さっきの一文の半分は理解できたも同然である。
基本の関数型インタフェース | メソッド | 説明 |
---|---|---|
Consumer<T> | void accept(T) | 引数を受け取って処理を行い結果を返さない、 「消費者」 |
Function<T,R> | R apply(T t) | 引数を受け取って、指定された型 (R)の結果を返す、 「処理」 |
Predicate<T> | boolean test(T t) | 引数を受け取ってそれをboolean値で評価する、 「断定」 |
Supplier<T> | T get() | 何も引数を受け取らずに結果だけを返す、 「供給者」 |
ここから分かってくる前提の話
さて、先ほどのルールを見返してみると「戻り値の型が一つに決められた〜〜」どうのこうの書いてある。
上の表をよく見てもらいたい。
そもそも戻り値を返さない
戻り値の型を決めなくていい
関数型インタフェースがあることに気づくだろう。
前者はConsumer<T>
、後者はPredicate<T>
であり、この二つはどうなるのか。
java.util.functionのJava SE APIドキュメントを見るとIntFunctionのようなプリミティブ特殊化型のIntConsumerやIntPredicateといった関数型インタフェースは存在している。では、applyAsIntのようにacceptAsIntやtestAsIntといったメソッドも存在しているのか?
答えはNoである。
今回問題になるのは、Function<T,R>とSupplier<T>の関連のメソッドであることをまずは押さえて頂きたい。(Goldの問題では、acceptAsIntやtestAsIntなんて選択肢にすら出てこない)
タイトルの問題を解いてみる
ここで本題に戻ろう。
Functionインタフェースのプリミティブ特殊化型であるIntFunctionのメソッドはただのapplyなのかapplyAsIntなのか・・・
ルールを読むと明白であるが、ポイントは戻り値の型が決まっているかどうかである.
これは関数型インタフェースの定義をジェネリクスまで見れば分かる。
Function関連のプリミティブ特殊化型には基本3パターンあるので、ここで確認してみよう。
IntFunction<R>
ToIntFunction<T>
DoubleToIntFunction
上記のジェネリクスの意味と、引数・戻り値の型の関係をざっくりまとめると
<R>:戻りの型を任意に決められる(引数はint型)
<T>:引数の型を任意に決められる(戻り値はint型)
ジェネリクスなし:引数も戻り値も型が決まっている(引数はdouble型、戻り値はint型)
となる。
つまり、タイトルのIntFunctionのメソッドは戻り値の型を任意で決めることができるので、ただのapply
ToIntFunctionとDoubleToIntFunctionは戻り値の型がint型で決まっているのでapplyAsInt
となるのだ。
もちろん、他のDoubleFunction(該当メソッドはapply)やToDoubleFunction(該当メソッドはapplyAsDouble)等々も、同じルールに従っている。
IntSupplierのメソッドは?
次はIntSupplierのメソッドについて考えてみよう。
IntFunctionのメソッドがただのapplyだったことを考えると、
IntSupplierのメソッドもただのgetだと思うかもしれない。
騙されてはいけない
Supplierが何者であったかをよく思い出さなければならない。
Supplier<T>とは何も引数を受け取らずに結果だけを返す、「供給者」
であった。
つまり、Supplier関連の関数型インタフェースには引数は存在しないのだから、プリミティブ特殊化型のIntSupplierでint型だと決めている部分は戻り値でしかありえないのである。
結果としてIntSupplierの該当メソッドはgetAsInt
ということになる。
逆にいうと該当メソッドがgetとなるのは、Supplier系においてはSupplier<T>しか存在しない。
(個人的にはSupplier<T>のジェネリクスはRにしたくなる)
応用問題
①BiFunction、②BinaryOperator、③IntBinaryOperator、④IntUnaryOperator、⑤ToDoubleBiFunction、それぞれの該当メソッドは何か?
まず①〜⑤はFuctionの特殊化型である。よって「〜該当メソッド=基本のメソッド+As+戻り値の型」の基本のメソッドの部分はapply
となる。
①BiFunction
定義をジェネリクスまで書くと、BiFunction<T,U,R>
簡単にいうとFunction<T,R>の引数が二つに増えたバージョン。
戻り値は決まっていないので該当メソッドはapply
(但し、引数は二つ存在する)
②BinaryOperator
定義をジェネリクスまで書くとBinaryOperator<T>
簡単にいうとBiFunctionの二つの引数、戻り値の型が全て同じ場合の関数型インタフェース。
BiFunction<T,T,T>と同義。
戻り値は決まっていないので該当メソッドはapply
(但し、引数は二つ存在する)
③IntBinaryOperator
BinaryOperatorの二つの引数および戻り値がint型のバージョン。
戻り値がint型で決まっているので該当メソッドは applyAsInt
(二つの引数もint型)
④IntUnaryOperator
まずUnaryOperator<T>
はFunctionの引数、戻り値の型が同じ場合の関数型インタフェース。
Function<T,T>と同義。
そのUnaryOperatorの引数および戻り値両方がint型のバージョン。
戻り値がint型で決まっているので該当メソッドはapplyAsInt
(引数もint型)
⑤ToDoubleBiFunction
定義をジェネリクスまで書くとToDoubleBiFunction<T,U>
戻り値だけdouble型だと決まっているので該当メソッドはapplyAsDouble
(但し、引数は二つ存在する)
最後に
ここまで書いたが、実装するときはコンパイルエラーが出るので、こんなまどろっこしいこと考えなくてもいいはず。この記事がGoldの試験勉強、java.util.functionパッケージの理解につながれば幸いだ。
参考
java.util.function以下の関数インターフェース使い方メモ
java.util.function (Java Platform SE 8 )