[英]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::isBlank
和p
有什么区别?
String::isBlank
和p
有什么区别?
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.