web-dev-qa-db-ja.com

なぜJava 8の述語<T>はFunction <T、Boolean>を拡張しないのですか?

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設計者はPredicateFunctionから完全に分離することを選択しましたか?そうすることを検討し、それに反対したという証拠はありますか?同様の質問だと思いますConsumer(_Function<T, Void>_)、Supplier(_Function<Void, T>_)、およびIntFunction(_Function<Integer, T>_)などのプリミティブ関数などの他のすべての「特別な」機能インターフェイスに適用されます。

私はこれのすべての影響についてあまり深く徹底的に考えていなかったので、おそらく何かが欠けています。

編集:いくつかの回答は、適用とテストの意味上の違いを強調しています。私はその区別に感謝しないと言っているのではなく、この区別があることが有益であることに同意します。私が理解していないのは、Predicateが_Function in_でもない理由です。たとえば、ListCollectionであるか、DoubleNumberであり、Objectです。

Predicate(およびConsumerSupplierIntUnaryOperatorなどの他のすべての特別な汎用機能インターフェイス)がFunctionとこの関係を持っている場合、Functionパラメーターが期待される場所でそれを使用できます(頭に浮かぶのは他の関数、たとえばmyFunction.compose(myPredicate)の呼び出し、または上記のような自動(アン)ボクシングの実装で十分な場合にAPIでいくつかの特殊な関数を記述しないようにする)

編集2:openjdkラムダプロジェクトを見ると、原始的な関数型インターフェースがFunction2012-12-19 のBrianGoetzからのこのコミットまで拡張するために使用されていることがわかりました。その頃、lambda-devまたはJSRエキスパートグループのメーリングリストで変更の具体的な理由を見つけることができませんでした。

39
mkadunc

Predicate<T>のメソッドはbooleanを返します。 Function<T, Boolean>のメソッドはBooleanを返します。それらは同じではありません。オートボクシングはありますが、Javaメソッドはプリミティブが使用する場合はラッパークラスを使用しません。また、Booleannullである可能性がありますがbooleanできません。

Consumer<T>の場合はさらに異なります。 Consumer<T>のメソッドの戻り値の型はvoidです。つまり、return;を使用して暗黙的に返すか返すことができますが、Function<T, Void>のメソッドはreturn null;を使用して返す必要があります。明示的に。

20
newacct

このような疑わしい継承階層は必要ありません。これらの機能インターフェイスは交換可能です。

Function<A,Boolean> f1=…;
Predicate<A>        p1=…;

Predicate<A>        p2=f1::apply;
Function<A,Boolean> f2=p1::test;

これは両方向で機能します。では、なぜ特定の方向性を宣伝する継承関係が必要なのでしょうか。

12
Holger

それはあなたの質問に対する直接の答えではありませんが、あなたはそれを何のために使うのでしょうか?

次のシナリオを検討してください。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()メソッドのみを期待するPredicatetest()メソッドを使用できるようにするのは直感的ではありません。
  • 関数型インターフェースは、oneタイプの基本機能(連鎖または論理演算を除く)のみを提供するように設計されています。状況によっては、CustomPredicateには2つのタイプの機能が含まれます。それは混乱を増すだけでしょう。
6
skiwi

私の意見では、_Function<T, R>_は単なるジェネリック関数の定義です。すべてのFunctionalInterfacesFunctionを実装する場合、唯一の抽象メソッドはapply()という名前にする必要があります。 FunctionalInterfaceのような具体的なFilterFileのコンテキストでは、抽象メソッドboolean accept(File pathname)Boolean apply(File)よりもはるかに優れた名前です。

注釈_@FunctionalInterface_は、インターフェースがFunctionalInterfaceとして使用できるように意図されていることをすでに示しています。それらすべてに基本インターフェースを実装させてから、一般的な方法でそれらを処理させることには利点はありません。 FunctionalInterfaceのセマンティクスを事前に気にせずに、それらすべてに対してapplyを呼び出すことができるようにする時期がわかりません。

6
Harmlezz