【问题标题】:Java 8 PredicatesJava 8 谓词
【发布时间】:2018-07-08 07:39:16
【问题描述】:

这是一个将 Predicate 作为参数的示例方法:

void processFields(List<WebDataField> fields, Predicate<WebDataField> predicate) {
    for (WebDataField webDataField : fields) {
        boolean expectedCondition = predicate.test(webDataField);
    }
}

我把上面的函数称为:

processFields(fields, w -> w.getFieldName().length() > 5);

另一个功能:

public void doSomething(Object someObject,Predicate<T> predicateInstance){
    //doSomething method's code goes here
}

在调用端,方法 doSomething() 将像这样调用 -

doSomething(someObjectInstance, Integer i-> i>10)

我检查了 Java 8 文档中的 String/Integer/Object 等,但没有找到任何实现 Predicate&lt;T&gt; 接口的类。我不知道我在 Java 8 教程系列中遗漏了什么,但我无法得到这种现象:

  1. 如果目标类型与 Predicate 类没有直接联系,编译器如何允许 lambda 表达式?

  2. 上述两种情况的答案是否相同?

【问题讨论】:

    标签: java generics lambda java-8


    【解决方案1】:

    lambda 表达式(Integer i) -&gt; i&gt;1 的值不是Integer,因此Integer 不必实现Predicate 以便您能够将该lambda 表达式传递给您的方法。

    Integer 是 lambda 表达式的参数 i 的类型。 (Integer i) -&gt; i&gt;1 可以表示具有单个方法的任何功能接口,该方法接受Integer 参数并返回boolean。因此它适合Predicate&lt;Integer&gt;功能接口。

    您的另一个 lambda 表达式 - w -&gt; w.getFieldName().length() &gt; 5 - 接受未指定类型的参数,并返回 boolean。为了让它通过编译,编译器必须将w 的类型推断为具有getFieldName() 方法的某个类或接口,该方法返回具有length() 方法的某种类型的实例。当您将 lambda 表达式传递给需要 Predicate&lt;WebDataField&gt;processFields 方法时,编译器可以推断 lambda 表达式的参数 w 的类型是 WebDataField

    【讨论】:

    • 好的,所以 (Integer i) -> i>1 将评估为布尔值,那么这如何通过编译?是布尔实现谓词吗?
    • @aka 将 lambda 表达式传递给方法(或将其分配给变量)不会执行该 lambda 表达式实现的功能接口的方法,因此 lambda 表达式的类型不是一个boolean,它是一个Predicate&lt;Integer&gt;
    【解决方案2】:

    回答第一个问题:
    上面的 lambda 表达式 i-&gt; i&gt;10 是表达以下内容的更短方式:

    Predicate<Integer> predicate = new Predicate<Integer>() {
    
      @Override
      public boolean test(Integer i) {
        return i > 10;
      }
    };
    

    然后按如下方式使用:

    Integer someObjectInstance = 1;
    doSomething(someObjectInstance, predicate);
    

    因此,目标类型 (Integer) 具有“与谓词的直接连接”。

    继续第二个问题:
    Predicate&lt;T&gt; 上,Tgeneric 类型参数。泛型类型参数只接受引用类型而不接受原始类型。因此Predicate&lt;T&gt; p 总是必须有一个对象实例作为T 类型的参数(你称之为someObjectInstance)。如果你想测试Integer,你可以这样做:

    Integer someObjectInstance = 1;
    doSomething(someObjectInstance, i-> i>10);
    

    但是,如果您尝试使用原始类型整数 (int) 调用该方法,则会出现语法错误:

    int integer = 0;
    worker.doSomething(integer, i -> i > 10); // The operator > is undefined for the argument type(s) Object, int
    

    但是您可以通过显式提供泛型类型参数来帮助编译器。在这种情况下,它是Integer,对应的包装类型为int。由于autoboxing,这将起作用。

    int integer = 0;
    worker.<Integer> doSomething(integer, i -> i > 10);
    

    但是您说“String/Integer/Object 等...没有找到任何实现谓词的类”。对于原始类型,有专用的谓词接口。例如:IntPredicate.
    但他们没有扩展接口Predicate&lt;T&gt;。这是不可能的,因为像 IntPredicate extends Predicate&lt;int&gt; 这样的定义是无效的。如上所述,int 不能作为泛型类型参数。

    要使用IntPredicate,您可以声明一个方法

      public <T> void doSomething(int i, IntPredicate intPredicate) {
        // doSomething method's code goes here
      }
    

    然后这样称呼它:

    doSomething(integer, i -> i > 10);
    

    要了解不同谓词接口的概述,请查看包 java.util.function reference

    【讨论】:

      猜你喜欢
      • 2021-10-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-01
      相关资源
      最近更新 更多