Java 8中谓词的含义和用法

4
我正在学习函数式接口、Lambda表达式和断言。我可以通过互联网上的例子编写程序,但我仍然不清楚某些结构。
这是我的类Employee,有3个数据成员,一个构造函数和相应的设置器和获取器。
package lambdaandrelated;

public class Employee {

    private String name;

    private String gender;

    private int age;

    public Employee(String name, String gender, int age) {
        super();
        this.name = name;
        this.gender = gender;
        this.age = age;
    }



    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

这是另一个具有3种检查方法的类:

a. 检查员工是否为男性。 b. 检查员工是否为女性。 c. 检查员工年龄是否大于传递的年龄。

package lambdaandrelated;

import java.util.function.Predicate;

    public class EmployeePredicates {

        public static Predicate<Employee> isMale(){
            return emp -> emp.getGender().equalsIgnoreCase("Male");  - **Line 1**
        }

        public static Predicate<Employee> isFemale(){
            return emp -> emp.getGender().equalsIgnoreCase("Female");
        }

        public Predicate<Employee> isGreaterThan(Integer age){
            return emp -> emp.getAge()>age;
        }
    }

这是我的主要类:
package lambdaandrelated;

import java.util.function.Predicate;

public class EmployeeMain {

    public static void main(String[] args) {
        Employee emp1 = new Employee("Parul", "Female", 24);
        Employee emp2 = new Employee("Kanika", "Female", 24);
        Employee emp3 = new Employee("Sumit", "Male", 27);


        Predicate<Employee> predicate1 = new EmployeePredicates().isGreaterThan(23);
        System.out.println(predicate1);  **Line2**
        boolean value = predicate1.test(emp3);
        System.out.println(value);

        boolean value1 = predicate1.negate().test(emp3);  **Line3**
        System.out.println(value1);

        System.out.println(predicate1.negate()); **Line4**



    }
}

我的疑问:

1)第一行是实现Predicate接口的test()方法吗?如果是,为什么isMale()方法的返回类型是Predicate<Employee>而不是boolean

2)第二行的输出即变量'predicate1'的值为" lambdaandrelated.EmployeePredicates$$Lambda$1/746292446@4617c264 "。但变量'predicate1'的类型是Predicate<Employee>。这里背后发生了什么?

3)什么是Predicate的否定?如果它意味着否定boolean输出,那么它不应该被应用于test()方法的输出,而应该应用于谓词本身(如第3行所做)。如果它意味着否定Predicate对象,那么为什么以及如何否定test()方法的输出?背后发生了什么,改变了truefalse的布尔值。是返回的谓词对象类型决定了test()方法的输出吗?

4)此外,第四行的输出也是"java.util.function.Predicate$$Lambda$2/2055281021@5ca881b5",而negate()的返回类型也是Predicate<T>,这似乎很好。那么为什么isMale()/isFemale()的输出格式不同?

3个回答

4
  1. isMale() is a method that returns a Predicate<Employee>, which is a functional interface having a single method that accepts an Employee and returns a boolean.

    The method doesn't return that boolean for a given Employee. It returns a function that can be applied on any Employee and return a boolean.

  2. When you print a reference variable, you see the runtime type of the object referenced by that variable, not its compile time type (this is, of course, assuming the runtime type doesn't override toString). A lambda expression is a way to implement a functional interface. If you implemented the interface explicitly with some class Predicate<Employee> predicate1 = new ClassThatImplementsPrdicateEmployee(), printing predicate1 would give you that name of that class ClassThatImplementsPrdicateEmployee and not Predicate. Similarly, in the case of a lambda expression, the compiler generates the returned name, similar to what happens with anonymous class instances.

  3. As for what negate does, look at the default implementation :

    default Predicate<T> negate() {
        return (t) -> !test(t);
    }
    

    It returns a Predicate whose implementation is the negation of applying the original Predicate's test() method on the given argument.


关于您的声明:“该方法不会为给定的员工返回布尔值。它返回一个可以应用于任何员工并返回布尔值的函数。” - 它返回一个函数还是 Predicate 类型的对象,您可以调用其中的 test() 方法,该方法的实现已通过 Lambda 表达式提供? - Random Coder
2
@parul 我所说的函数是函数式编程中使用的术语。是的,在Java中,支持函数式编程的底层使用了函数接口(例如Predicate),可以使用lambda表达式、方法引用、匿名类实例或常规类实例来实现。 - Eran

4

针对第一点:

你可以稍微改写一下,也许会更有意义:

public static Predicate<Employee> isMale() {
    return new Predicate<Employee>() {
        @Override
        public boolean test(Employee emp) {
            return emp.getGender().equalsIgnoreCase("Male");
        }
    };
}

boolean必须作为Predicate#test的返回类型。而这个结构:

 return emp -> emp.getGender().equalsIgnoreCase("Male");

实际上,返回一个具有返回布尔值的测试方法的Predicate

对于第 2 点

当编译器看到 lambda 函数时,它会将其“解糖”,并在您实际编写此 Predicate 的类中创建一个实例方法。

因此,在您的 EmployeePredicates 中,您将拥有一些类似于以下的方法(您可以通过 javap 反编译并查看这些方法):

private static boolean lambda$isFemale$0(Employee employee ){
    // your isFemale logic here
}

然后会在运行时为您生成一个名为EmployeePredicates$$Lambda$1的类,该类将调用此方法。您可以通过以下方式再次查看此类名称和代码:

-Djdk.internal.lambda.dumpProxyClasses=/Your/Path

所以这就是类的来源,名称是由编译器生成的。
对于第三点,基本上是指对于Predicate而言不是使用==,而是使用!=。如果查看negate的实现,则应清楚此内容。
default Predicate<T> negate() {
    return (t) -> !test(t);
}

应用测试,然后否定结果。


1
对于第3个问题 - 我可以得出结论,negate() 创建了一个新的 Predicate,其 test() 方法的实现等于调用 negate() 的 Predicate 的 test() 方法的否定。 - Random Coder
@parul 我认为在 negate() 上创建并返回了一个新的谓词,当你返回一个新的 lambda 时就会发生这种情况。 - fps

1
1) 每当您使用lambda时,都会创建一个函数。在这种情况下,您指定该函数应为Predicate类型。由于接口Predicate只有一个要实现的方法,“->”后面的部分是该函数的实现。
2) println将调用该对象上的toString(),该对象是Predicate对象。Lambda将具有toString方法的默认实现,这有助于您找到它的创建位置。
3) .negate()将创建一个全新的谓词,反过来可以进行测试。它将返回相反的值。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接