4

可以像这样链接/连接对 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;
});
4

3 回答 3

9

可以通过功能接口的默认方法进行链接。但是“问题”是,当您返回合成表达式的右侧时,推理引擎没有足够的信息来确定左侧是相同的功能接口。

要提供该信息,您必须转换声明:

  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
}
于 2015-07-25T13:29:38.843 回答
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你已经离开了第二个变体。那么我们为什么要有这样一种替代形式呢?

方法引用已被发明为允许单个方法委托的密集语法的功能,声明和引用参数可以真正产生影响。即使用鞋底替换鞋底s->System.out.println(s)也不System.out::println是什么大胜利,但至少有一些。此外,在字节码级别对方法引用进行编码可以更紧凑,因为可以直接引用目标方法,就像保存 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);
于 2015-07-27T12:50:04.123 回答
3

不,您不能按照您的建议使用方法引用。方法引用实际上只是 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 表达式,不能用作块中的独立语句。

于 2015-07-25T11:26:13.807 回答