为什么Java 8的Predicate <T>不扩展Function <T,Boolean>

mka*_*unc 38 java lambda functional-programming predicate java-8

如果我编写Predicate接口,我想在接口中编码它只是一个返回原始布尔值的函数,如下所示:

@FunctionalInterface
public interface Predicate<T> extends Function<T, Boolean> {

    boolean test(T t);

    @Override
    default Boolean apply(T t) {
        return Boolean.valueOf(test(t));
    }
}
Run Code Online (Sandbox Code Playgroud)

我想知道,有没有令人信服的理由让Java 8 API设计者选择将Predicate与Function完全分开?是否有一些证据表明他们认为这样做并决定反对呢?我想类似的问题适用于所有其他'特殊'功能接口,如Consumer(可能是Function <T,Void>),Supplier(Function <Void,T>)和原始函数,如IntFunction(Function <Integer,T>).

我没有深入和彻底地考虑过这个问题的所有后果,所以我可能会遗漏一些东西.

编辑:一些答案强调了应用和测试之间的语义区别.我不是说我不欣赏这种区别,我同意这种区别是有益的.我不明白的是,为什么Predicate不是一个函数,就像List是一个Collection或Double是一个Number,它是一个Object.

如果Predicate(以及所有其他特殊的通用功能接口,例如Consumer,Supplier,IntUnaryOperator等)与Function有这种关系,它将允许人们在预期使用Function参数的地方使用它(想到的是组合与其他功能,例如,呼叫myFunction.compose(比如说myPredicate)或避免在API中写入几个专门的功能时,如上所述的这种汽车(未)拳击实施将是足够的)

编辑2:看看openjdk lambda项目,我发现原始的功能接口用于扩展功能,直到2012-12-19的Brian Goetz提交.我找不到具体的原因,在那个时候对任何lambda-dev或JSR专家组邮件列表进行了更改.

new*_*cct 19

Predicate<T>返回的方法boolean.Function<T, Boolean>返回的方法Boolean.他们不一样.虽然存在自动装箱,但是当原语可以使用时,Java方法不使用包装类.此外,有喜欢的差异Boolean可以null同时boolean不能.

在这种情况下,它更加不同Consumer<T>.Consumer<T>具有返回类型的方法void,这意味着它可以隐式返回或返回使用return;,但是Function<T, Void>必须使用return null;显式返回方法.

  • 这听起来并不令人信服.考虑到Java 8有默认方法,他们难道不能轻易地制作`Predicate <T>`extend`Punction <T>`并确保谓词中的`default apply`方法委托给`test`?这样消费者可以简单地实现`test`,同时也可以获得一致的`apply`语义 (13认同)

Hol*_*ger 12

不需要这种可疑的继承层次结构.这些功能接口是可互换的.

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

Predicate<A>        p2=f1::apply;
Function<A,Boolean> f2=p1::test;
Run Code Online (Sandbox Code Playgroud)

这适用于两个方向.那么为什么要有一个继承关系来宣传一个特定的方向呢?


ski*_*iwi 6

这不是你问题的直接答案,而是你将用它做什么?

请考虑以下情形:您希望将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);
    }
}
Run Code Online (Sandbox Code Playgroud)
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));
Run Code Online (Sandbox Code Playgroud)

然而,以下内容告诉我他们肯定会想到类似的东西,因为它们提供了分区方法:

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));
Run Code Online (Sandbox Code Playgroud)

我能想到的一些原因是:

  • apply()在一个Predicate你只期望一个test()方法的地方有一个方法是不直观的.
  • 功能接口旨在提供仅一种类型的基本功能(不包括链接或逻辑操作),在您的情况下CustomPredicate包含两种类型的功能.这只会增加混乱.


Har*_*ezz 6

在我看来Function<T, R>,只是通用功能的定义.如果全部FunctionalInterfaces实现Function,则必须命名唯一的抽象方法apply().在具体的情境FunctionalInterface一样FilterFile,抽象方法boolean accept(File pathname)是一个更好的名字,那么Boolean apply(File).

注释@FunctionalInterface已将接口标记为可用作a FunctionalInterface.除了以通用方式处理它们之外,它们都实现基本接口是没有好处的.我不知道你什么时候不关心它的语义,FunctionalInterface以便让它们全部可用apply.

  • 我不一定主张所有功能接口都应该用Function替换.我建议预定义的通用功能接口可以扩展Function,这将保留语义,同时允许其他内容,例如通用中的处理(这似乎是一个相关的好处,特别是如果没有明显的缺点) (2认同)