问题描述
可以链接/连接lambda表达式中的元素所执行的操作,如下所示:
list.forEach(s -> {
System.out.println(s.toLowerCase());
System.out.println(s.toUpperCase());
});
有没有办法用方法引用来做到这一点? 像这样的东西:
list.forEach({
System.out::println(String::toLowerCase);
System.out::println(String::toCase);
});
我知道我可以在四个单独的调用中执行此操作(这也会更多,这会改变值):
list.replaceAll(String::toLowerCase);
list.forEach(System.out::println);
list.replaceAll(String::toUpperCase);
list.forEach(System.out::println);
我甚至不能像这样轻松做事:
list.forEach({
System.out::println;
System.out::println;
});
1楼
通过功能接口的默认方法可以链接。 但是“问题”在于,当您返回合成表达式的右侧时,推理引擎没有足够的信息来确定左侧是相同的功能接口。
要提供该信息,您必须转换声明:
List<String> l = Collections.emptyList();
l.forEach(((Consumer<String>)System.out::println).andThen(System.out::println));
或者首先将其分配给变量:
Consumer<String> cons = System.out::println;
Collections.<String>emptyList().forEach(cons.andThen(System.out::println));
或者,您也可以编写可以执行所需操作的静态帮助程序方法
Collections.<String>emptyList().forEach(combine(System.out::println, System.out::println));
static <T> Consumer<T> combine(Consumer<T>... consumers) {
// exercise left to the reader
}
2楼
不,你不能像你建议的那样使用方法引用。 方法引用实际上只是lambda表达式的语法替代。 所以,而不是:
text -> console.print(text)
您可以避免引入不必要的变量而是使用
console::print
所以当你提到你不能做的事情,比如:
list.forEach({
System.out::println;
System.out::println;
});
这只是一个语法快捷方式
list.forEach({
c -> System.out.println(c);
c -> System.out.println(c);
});
这没有任何意义。 没有表示列表中项目的变量(必须在块之外),并且两个'语句'是lambda表达式,没有任何内容可以应用。
方法引用是一个非常简洁的快捷方式,可以避免使用不必要的变量,但它们只是一个更详细的lambda表达式的替代,不能用作块中的独立语句。
3楼
转换没有意义
list.forEach(s -> {
System.out.println(s.toLowerCase());
System.out.println(s.toUpperCase());
});
至
list.forEach({
System.out::println(String::toLowerCase);
System.out::println(String::toUpperCase);
});
因为在清晰度上没有胜利,后者甚至包含比前者更多的字符,如果我们使用相同的缩进并插入Upper
那么你已经离开了第二个变体。
那么我们为什么要有这样一种替代形式呢?
方法参考已被发明为允许单个方法委派的密集语法的特征,声明和引用参数确实可以产生影响。
即使用System.out::println
替换唯一的s->System.out.println(s)
也没有那么大的胜利,但至少有一些。
此外,在字节码级别对方法引用进行编码可以更紧凑,因为目标方法可以直接引用,就像保存lambda表达式代码的合成方法一样。
对于复合方法参考,没有这种紧凑的形式。
由于您所需的操作包含不同类型的操作,因此您可以使用Stream
API,它旨在组合这些操作:
list.stream().flatMap(s->Stream.of(s.toLowerCase(), s.toUpperCase()))
.forEach(System.out::println);
如果您想不惜一切代价包含所有内容的方法引用,您可以通过以下方式进行:
list.stream()
.flatMap(s->Stream.<UnaryOperator<String>>of(String::toLowerCase, String::toUpperCase)
.map(f->f.apply(s)))
.forEach(System.out::println);