我对以下代码感到困惑
class LambdaTest {
public static void main(String[] args) {
Consumer<String> lambda1 = s -> {};
Function<String, String> lambda2 = s -> s;
Consumer<String> lambda3 = LambdaTest::consume; // but s -> s doesn't work!
Function<String, String> lambda4 = LambdaTest::consume;
}
static String consume(String s) { return s;}
}
我本来预计 lambda3 的分配会失败,因为我的消耗方法与使用者接口(interface)中的接受方法不匹配 - 返回类型不同,字符串与 void。
此外,我一直认为 Lambda 表达式和方法引用之间存在一对一的关系,但正如我的示例所示,这显然不是这种情况。
有人可以向我解释这里发生了什么吗?
最佳答案
如Brian Goetz指出in a comment ,设计决策的基础是允许以与调用方法相同的方式使方法适应功能接口(interface),即您可以调用每个值返回方法并忽略返回值。
当涉及到 lambda 表达式时,事情变得有点复杂。有两种形式的 lambda 表达式,(args) -> expression
和 (args) -> { statements* }
.
第二种形式是否为void
兼容,取决于是否没有代码路径尝试返回值的问题,例如() -> { return ""; }
不是 void
兼容,但表达式兼容,而 () -> {}
或 () -> { return; }
是 void
兼容的。请注意 () -> { for(;;); }
和 () -> { throw new RuntimeException(); }
两者都是,void
兼容和价值兼容,因为它们不能正常完成并且没有 return
陈述。
表格(arg) -> expression
如果表达式计算为一个值,则值兼容。但也有表达式,它们同时是语句。这些表达式可能有副作用,因此可以编写为仅产生副作用的独立语句,而忽略产生的结果。同样,表格 (arg) -> expression
可以是void
兼容,如果表达式也是一个语句。s -> s
形式的表达式不能是 void
兼容 s
不是陈述,即你不能写 s -> { s; }
任何一个。另一方面s -> s.toString()
可以是void
兼容,因为方法调用是语句。同样,s -> i++
可以是void
compatible as 增量可以用作语句,所以s -> { i++; }
也是有效的。当然,i
必须是一个字段才能工作,而不是局部变量。
Java 语言规范 §14.8. Expression Statements列出所有可用作语句的表达式。除了已经提到的方法调用和递增/递减运算符之外,它还命名了赋值和类实例创建表达式,所以 s -> foo=s
和 s -> new WhatEver(s)
是 void
也兼容。
作为旁注,表格(arg) -> methodReturningVoid(arg)
是唯一不值兼容的表达式形式。
关于java - 为什么带有返回类型的 Java 方法引用与 Consumer 接口(interface)匹配?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37308294/