为什么Java 8的谓词扩展函数 ?

时间:2021-11-05 02:38:00

If I wrote the Predicate interface, i'd want to encode in the interface the fact that it's just a function that returns a primitive boolean, like this:

如果我写了谓词接口,我想在接口中编码它只是一个返回原始布尔值的函数,像这样

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

    boolean test(T t);

    @Override
    default Boolean apply(T t) {
        return Boolean.valueOf(test(t));
    }
}

I was wondering, is there a compelling reason Java 8 API designers chose to keep the Predicate completely separate from Function? Is there some evidence that they considered doing so and decided against it? I guess similar question goes for all the other 'special' functional interfaces like Consumer (could be Function<T, Void>), Supplier (Function<Void, T>) and primitive functions like IntFunction (Function<Integer, T>).

我想知道,Java 8 API设计人员是否选择将谓词与函数完全分离,这是一个令人信服的原因吗?有没有证据表明他们考虑过这样做并决定反对?我想,类似的问题也适用于所有其他的“特殊”功能接口,比如消费者(可以是函数 ),供应商(函数 )和原始函数如IntFunction(函数 )。 ,> ,> ,>

I haven't thought very deeply and thoroughly about all the ramifications of this, so I'm probably missing something.

我还没有深入透彻地思考这一切的后果,所以我可能漏掉了什么。

EDIT: Some of the answers ephasize the semantic distinction between apply and test. I'm not saying I don't appreciate the distinction, and I agree that it's beneficial to have this distinction. What I don't understand is why a Predicate is nevertheless not also a Function in the same way as e.g. a List is a Collection or Double is a Number, which is an Object.

编辑:一些答案ephasize在应用和测试之间的语义区别。我并不是说我不欣赏这种区别,我同意有这种区别是有益的。我不明白的是,为什么谓词也不是一个函数,就像List是一个集合,或者Double是一个数字,它是一个对象。

If Predicate (and all the other special generic functional interfaces, such as Consumer, Supplier, IntUnaryOperator etc.) had this relation with Function, it would allow one to use it in place where Function parameter is expected (what comes to mind is composition with other functions, e.g. calling myFunction.compose(myPredicate) or to avoid writing several specialized functions in an API when such auto(un)boxing implementation as described above would be sufficient)

如果谓词(和所有其他特殊通用功能接口,如消费者,供应商,IntUnaryOperator等等)有这个关系函数,将允许一个预计在函数参数的地方使用它(是什么成分与其他功能,如调用myFunction.compose(myPredicate)或避免写几个专门的函数在一个API等汽车(联合国)拳击实现上面描述就足够了)

EDIT 2: Looking at openjdk lambda project I found that primitive functional interfaces used to extend Function up until this commit from Brian Goetz on 2012-12-19. I couldn't find specific reasons for the change on any of the lambda-dev or JSR experts group mailing lists around that time.

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

4 个解决方案

#1


18  

The method in Predicate<T> returns boolean. The method in Function<T, Boolean> returns Boolean. They are not the same. Although there is autoboxing, Java methods don't use wrapper classes when primitives would do. Also, there are differences like Boolean can be null while boolean can't.

谓词 的方法返回布尔值。函数 返回布尔值。它们不一样。虽然有自动装箱,但是Java方法在原语使用时不使用包装器类。同样,也有一些不同,比如布尔值可以是null,而布尔值不能。 ,>

It's even more different in the case of Consumer<T>. The method in Consumer<T> has return type void, which means it can implicitly return or return using return;, but the method in Function<T, Void> must return using return null; explicitly.

在消费者的情况下,它甚至更不同于>。消费者 的方法有返回类型void,这意味着它可以使用return隐式返回或返回;但是函数 必须返回使用return null;明确。 ,>

#2


13  

There is no need for such a suspicious inheritance hierarchy. These functional interfaces are inter-changable.

这样一个可疑的继承层次结构是不需要的。这些功能接口是可交换的。

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

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

This works in both directions. So why should there be an inheritance relationship advertising one specific direction?

这是双向的。那么,为什么要有一种针对特定方向的继承关系呢?

#3


6  

In my opinion Function<T, R> is just the definition of a generic function. If all FunctionalInterfaces would implement Function, the only abstract method would have to be named apply(). In the context of a concrete FunctionalInterface like FilterFile, the abstract method boolean accept(File pathname) is a much better name then Boolean apply(File).

在我的观点中,函数 只是一个泛型函数的定义。如果所有的functionalinterface都实现了函数,那么唯一的抽象方法就必须被命名为apply()。在像FilterFile这样的具体函数接口的上下文中,抽象方法布尔accept(文件路径名)是一个更好的名称,然后是布尔apply(文件)。 ,>

The annotation @FunctionalInterface already marks an interface as being intended to be usable as a FunctionalInterface. There is no benefit having them all implement a base interface other then processing them in a generic way. I do not see when you would not care about the semantics of a FunctionalInterface in advance to make them available to call apply for them all.

注解@FunctionalInterface已经标记了一个接口,它被用来作为一个功能接口。让它们都实现一个基本接口,然后以通用的方式处理它们是没有好处的。我看不出您什么时候才会关心功能接口的语义,以便让它们都可以调用应用程序。

#4


5  

It is not a direct answer to your question, but for what would you be using it for?

这不是对你的问题的直接回答,而是你会用它做什么?

Consider the following scenario: You want to map true/false to the list of values for which it is true respectively false.

考虑下面的场景:您希望将true/false映射到其为true的值列表。

With your code you could use:

你可以用你的代码:

@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));

The following however tells me that they have definately thought about something similar, as they offer the partitioning method:

但是,下面的内容告诉我,他们已经明确地考虑过类似的事情,因为他们提供了分区方法:

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));

Some reasons I can think about are:

我能想到的一些原因是:

  • It would not be intuitive to have a apply() method available in a Predicate where you only expect a test() method.
  • 在谓词中有一个apply()方法是不太直观的,您只需要一个test()方法。
  • Functional interfaces are designed to provide only one type of basic functionality (excluding chaining or logical operations), in your situation CustomPredicate contains two types of functionality. It would only add to confusion.
  • 功能接口被设计为只提供一种基本功能(不包括链接或逻辑操作),在您的情况下,CustomPredicate包含两种类型的功能。这只会增加混乱。

#1


18  

The method in Predicate<T> returns boolean. The method in Function<T, Boolean> returns Boolean. They are not the same. Although there is autoboxing, Java methods don't use wrapper classes when primitives would do. Also, there are differences like Boolean can be null while boolean can't.

谓词 的方法返回布尔值。函数 返回布尔值。它们不一样。虽然有自动装箱,但是Java方法在原语使用时不使用包装器类。同样,也有一些不同,比如布尔值可以是null,而布尔值不能。 ,>

It's even more different in the case of Consumer<T>. The method in Consumer<T> has return type void, which means it can implicitly return or return using return;, but the method in Function<T, Void> must return using return null; explicitly.

在消费者的情况下,它甚至更不同于>。消费者 的方法有返回类型void,这意味着它可以使用return隐式返回或返回;但是函数 必须返回使用return null;明确。 ,>

#2


13  

There is no need for such a suspicious inheritance hierarchy. These functional interfaces are inter-changable.

这样一个可疑的继承层次结构是不需要的。这些功能接口是可交换的。

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

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

This works in both directions. So why should there be an inheritance relationship advertising one specific direction?

这是双向的。那么,为什么要有一种针对特定方向的继承关系呢?

#3


6  

In my opinion Function<T, R> is just the definition of a generic function. If all FunctionalInterfaces would implement Function, the only abstract method would have to be named apply(). In the context of a concrete FunctionalInterface like FilterFile, the abstract method boolean accept(File pathname) is a much better name then Boolean apply(File).

在我的观点中,函数 只是一个泛型函数的定义。如果所有的functionalinterface都实现了函数,那么唯一的抽象方法就必须被命名为apply()。在像FilterFile这样的具体函数接口的上下文中,抽象方法布尔accept(文件路径名)是一个更好的名称,然后是布尔apply(文件)。 ,>

The annotation @FunctionalInterface already marks an interface as being intended to be usable as a FunctionalInterface. There is no benefit having them all implement a base interface other then processing them in a generic way. I do not see when you would not care about the semantics of a FunctionalInterface in advance to make them available to call apply for them all.

注解@FunctionalInterface已经标记了一个接口,它被用来作为一个功能接口。让它们都实现一个基本接口,然后以通用的方式处理它们是没有好处的。我看不出您什么时候才会关心功能接口的语义,以便让它们都可以调用应用程序。

#4


5  

It is not a direct answer to your question, but for what would you be using it for?

这不是对你的问题的直接回答,而是你会用它做什么?

Consider the following scenario: You want to map true/false to the list of values for which it is true respectively false.

考虑下面的场景:您希望将true/false映射到其为true的值列表。

With your code you could use:

你可以用你的代码:

@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));

The following however tells me that they have definately thought about something similar, as they offer the partitioning method:

但是,下面的内容告诉我,他们已经明确地考虑过类似的事情,因为他们提供了分区方法:

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));

Some reasons I can think about are:

我能想到的一些原因是:

  • It would not be intuitive to have a apply() method available in a Predicate where you only expect a test() method.
  • 在谓词中有一个apply()方法是不太直观的,您只需要一个test()方法。
  • Functional interfaces are designed to provide only one type of basic functionality (excluding chaining or logical operations), in your situation CustomPredicate contains two types of functionality. It would only add to confusion.
  • 功能接口被设计为只提供一种基本功能(不包括链接或逻辑操作),在您的情况下,CustomPredicate包含两种类型的功能。这只会增加混乱。