繁体   English   中英

使用 Predicate 方法 - “此表达式的目标类型必须是功能接口”

[英]Using Predicate method - "The target type of this expression must be a functional interface"

第三行不会编译,因为The target type of this expression must be a functional interface

Predicate<String> p = String::isBlank;
List.of("").stream().filter(p.negate());
List.of("").stream().filter((String::isBlank).negate()); // compile error

为什么不? String::isBlankp有什么区别?

String::isBlankp有什么区别?

p有一个类型。 String::isBlank没有。

来自 Java 语言规范, 方法参考的类型

如果T是功能接口类型(第 9.8 节)并且表达式与从T派生的地面目标类型的 function 类型一致,则方法引用表达式在赋值上下文、调用上下文或转换上下文中与目标类型T兼容.

然后它继续定义“全等”和“地面目标类型”的含义。 与您的问题相关的部分是,当您这样编写String::isBlank ,它不在赋值上下文、调用上下文或强制转换上下文中:

(String::isBlank).negate()

您可能认为这算作调用上下文,但调用上下文实际上是方法调用的 arguments ,例如someMethod(String::isBlank) 正如规范所说:

调用上下文允许将方法或构造函数调用(§8.8.7.1、§15.9、§15.12)中的参数值分配给相应的形式参数。

无论如何,因为String::isBlank不在这些上下文中,所以规范没有说明它的类型。 事实上, 就在页面上一点,它说,

如果方法引用表达式出现在程序中除赋值上下文(第 5.2 节)、调用上下文(第 5.3 节)或强制转换上下文(第 5.5 节)之外的某个地方,则属于编译时错误。

所以这些上下文是唯一可以出现方法引用的上下文!

我没有设计 Java 语言,所以我不能告诉你设计者在设计这个时是怎么想的,但我知道这个设计实现起来相当简单,比方说,允许到处引用方法,这需要考虑更多的边缘情况,以及这将如何与其他语言功能交互。 如果方法引用只限于这三个上下文,就不会有那么多边缘情况需要考虑,而且比较容易弄清楚它们是什么类型,如果你想在某处的随机表达式中使用它们,这种设计还允许您通过强制转换来做到这一点:

((Predicate<String>)String::isBlank).negate()

在我看来,这是一个相当不错的折衷方案。

如果您仍然想知道“他们为什么不像……那样实现它?” 我建议在这里查看 Eric Lippert 的答案

Lambda 表达式方法引用本身没有类型 它们是所谓的poly 表达式,这意味着它们的解释方式取决于它们出现的上下文。

例如,在下面的语句中,方法引用String::isBlank符合Consumer接口。 它将成功编译并执行:

List.<String>of().forEach(String::isBlank);

在以下语句中,类型或引用String::isBlank将被推断为Function<String>

Stream.<String>of().map(String::isBlank);

当您尝试将方法链接到本身没有类型的东西上时,编译器会感到困惑,它没有信息来推断String::isBlank可以在(String::isBlank).negate()中符合哪个功能接口从整体表达式预期为Predicate的事实来看。 如果您期望方法negate()会为编译器提供线索,那么这个假设是错误的,它不是方法解析在 Java 中的工作方式。 首先,编译器需要知道类型,然后它才会在属于这种类型的方法中寻找潜在适用的方法,而不是相反。

如果你想这样使用它,你需要提供一个上下文 方法引用应该出现在这些类型的上下文中:赋值上下文、调用上下文、强制转换上下文

这是我们如何使用调用上下文的示例,以下代码编译:

List.of("").stream().filter(m(String::isBlank).negate());
public static <T> Predicate<T> m(Predicate<T> predicate) {
    return predicate;
}

这是@Jesper评论中已经发布的强制转换上下文示例:

filter(((Predicate<? super String>) (String::isBlank)).negate())

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM