多个 lambda 方法引用

Multiple lambda method references

可以chain/concatenate像这样对 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;
});

不,您不能按照您的建议使用方法引用。方法引用实际上只是 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);
});

这真没道理。没有代表列表中项目的变量(必须在块之外)并且两个 'statements' 是 lambda 表达式,没有任何应用。

方法引用是避免不必要变量的一种非常巧妙的捷径,但它们只是更冗长的 lambda 表达式的替代品,不能用作块中的独立语句。

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

要提供该信息,您必须执行以下语句:

  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
}

转换没有意义

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);