Predicate
インターフェイスを作成した場合、次のように、プリミティブboolean
を返す関数であるという事実をインターフェイスにエンコードしたいと思います。
_@FunctionalInterface
public interface Predicate<T> extends Function<T, Boolean> {
boolean test(T t);
@Override
default Boolean apply(T t) {
return Boolean.valueOf(test(t));
}
}
_
疑問に思っていたのですが、説得力のある理由がありますかJava 8 API設計者はPredicate
をFunction
から完全に分離することを選択しましたか?そうすることを検討し、それに反対したという証拠はありますか?同様の質問だと思いますConsumer
(_Function<T, Void>
_)、Supplier
(_Function<Void, T>
_)、およびIntFunction
(_Function<Integer, T>
_)などのプリミティブ関数などの他のすべての「特別な」機能インターフェイスに適用されます。
私はこれのすべての影響についてあまり深く徹底的に考えていなかったので、おそらく何かが欠けています。
編集:いくつかの回答は、適用とテストの意味上の違いを強調しています。私はその区別に感謝しないと言っているのではなく、この区別があることが有益であることに同意します。私が理解していないのは、Predicate
が_Function in
_でもない理由です。たとえば、List
はCollection
であるか、Double
はNumber
であり、Object
です。
Predicate
(およびConsumer
、Supplier
、IntUnaryOperator
などの他のすべての特別な汎用機能インターフェイス)がFunction
とこの関係を持っている場合、Function
パラメーターが期待される場所でそれを使用できます(頭に浮かぶのは他の関数、たとえばmyFunction.compose(myPredicate)
の呼び出し、または上記のような自動(アン)ボクシングの実装で十分な場合にAPIでいくつかの特殊な関数を記述しないようにする)
編集2:openjdkラムダプロジェクトを見ると、原始的な関数型インターフェースがFunction
を 2012-12-19 のBrianGoetzからのこのコミットまで拡張するために使用されていることがわかりました。その頃、lambda-devまたはJSRエキスパートグループのメーリングリストで変更の具体的な理由を見つけることができませんでした。
Predicate<T>
のメソッドはboolean
を返します。 Function<T, Boolean>
のメソッドはBoolean
を返します。それらは同じではありません。オートボクシングはありますが、Javaメソッドはプリミティブが使用する場合はラッパークラスを使用しません。また、Boolean
はnull
である可能性がありますがboolean
できません。
Consumer<T>
の場合はさらに異なります。 Consumer<T>
のメソッドの戻り値の型はvoid
です。つまり、return;
を使用して暗黙的に返すか返すことができますが、Function<T, Void>
のメソッドはreturn null;
を使用して返す必要があります。明示的に。
このような疑わしい継承階層は必要ありません。これらの機能インターフェイスは交換可能です。
Function<A,Boolean> f1=…;
Predicate<A> p1=…;
Predicate<A> p2=f1::apply;
Function<A,Boolean> f2=p1::test;
これは両方向で機能します。では、なぜ特定の方向性を宣伝する継承関係が必要なのでしょうか。
それはあなたの質問に対する直接の答えではありませんが、あなたはそれを何のために使うのでしょうか?
次のシナリオを検討してください。true/ falseを、それぞれtrueまたはfalseである値のリストにマップする必要があります。
あなたのコードであなたは使うことができます:
_@FunctionalInterface
interface CustomPredicate<T> extends Function<T, Boolean> {
boolean test(T value);
@Override
default Boolean apply(T t) {
return test(t);
}
}
_
_List<String> stringList = new ArrayList<>();
stringList.add("a");
stringList.add("hg");
stringList.add("dsl");
stringList.add("sldi");
stringList.add("ilsdo");
stringList.add("jlieio");
CustomPredicate<String> customPredicate = str -> (str.length() >= 3);
Map<Boolean, List<String>> mapping = stringList.stream()
.collect(Collectors.groupingBy(customPredicate));
_
ただし、以下は、パーティショニング方法を提供しているため、同様のことを確実に考えていることを示しています。
_List<String> stringList = new ArrayList<>();
stringList.add("a");
stringList.add("hg");
stringList.add("dsl");
stringList.add("sldi");
stringList.add("ilsdo");
stringList.add("jlieio");
Predicate<String> predicate = str -> (str.length() >= 3);
Map<Boolean, List<String>> mapping = stringList.stream()
.collect(Collectors.partitioningBy(predicate));
_
私が考えることができるいくつかの理由は次のとおりです。
apply()
メソッドのみを期待するPredicate
でtest()
メソッドを使用できるようにするのは直感的ではありません。CustomPredicate
には2つのタイプの機能が含まれます。それは混乱を増すだけでしょう。私の意見では、_Function<T, R>
_は単なるジェネリック関数の定義です。すべてのFunctionalInterfaces
がFunction
を実装する場合、唯一の抽象メソッドはapply()
という名前にする必要があります。 FunctionalInterface
のような具体的なFilterFile
のコンテキストでは、抽象メソッドboolean accept(File pathname)
はBoolean apply(File)
よりもはるかに優れた名前です。
注釈_@FunctionalInterface
_は、インターフェースがFunctionalInterface
として使用できるように意図されていることをすでに示しています。それらすべてに基本インターフェースを実装させてから、一般的な方法でそれらを処理させることには利点はありません。 FunctionalInterface
のセマンティクスを事前に気にせずに、それらすべてに対してapply
を呼び出すことができるようにする時期がわかりません。