Multiple lambda method references

喜你入骨 提交于 2019-11-29 11:34:33

chaining is possible through default methods of the functional interfaces. But the "problem" is that there that the inference engine does not have enough information to determine that the left hand side is the same functional interface when you're returning the right hand side of the compositing expression.

To provide that information you either have to cast the statement:

  List<String> l = Collections.emptyList();
  l.forEach(((Consumer<String>)System.out::println).andThen(System.out::println));

Or assign it to a variable first:

  Consumer<String> cons = System.out::println;
  Collections.<String>emptyList().forEach(cons.andThen(System.out::println));

Alternatively you could also write static helper methods that do what you want

Collections.<String>emptyList().forEach(combine(System.out::println, System.out::println));

static <T> Consumer<T> combine(Consumer<T>... consumers) {
    // exercise left to the reader
}

No you can't use method references as you have suggested. Method references are really just a syntactic replacement for a lambda expression. So that instead of:

text -> console.print(text)

You can avoid introducing an unnecessary variable and instead use

console::print

So when you mention that you can't do something such as:

list.forEach({
    System.out::println;
    System.out::println;
});

This is just a syntactic shortcut for

list.forEach({
    c -> System.out.println(c);
    c -> System.out.println(c);
});

This really makes no sense. There's no variable representing the item in the list (which would have to be outside the block) and the two 'statements' are lambda expressions with nothing to be applied to.

Method references are a pretty neat shortcut to avoid having an unnecessary variable but they are just a substitute for a more verbose lambda expression and can't be used as an independent statement in a block.

There is no point in converting

list.forEach(s -> {
    System.out.println(s.toLowerCase());
    System.out.println(s.toUpperCase());
});

to

list.forEach({
    System.out::println(String::toLowerCase);
    System.out::println(String::toUpperCase);
});

as there is no win in clarity, the latter even consists of more characters than the former, if we use the same indention and insert the Upper you have left off the second variant. So why should we have such an alternative form?

Method reference have been invented as a feature allowing a dense syntax for a single method delegation, were declaring and referencing the parameters could really make a difference. Even replacing a sole s->System.out.println(s) with System.out::println is no that big win, but at least, there is some. Further, encoding the method reference at bytecode level can be more compact because the target method can be directly referenced just like the synthetic method holding a lambda expression’s code. For compound method references, there is no such compact form.


Since your desired operation consist of operations of different kinds, you may use the Stream API, which is intended to combine such operations:

list.stream().flatMap(s->Stream.of(s.toLowerCase(), s.toUpperCase()))
    .forEach(System.out::println);

and if you want to include method references for everything, at all costs, you may do it the following way:

list.stream()
    .flatMap(s->Stream.<UnaryOperator<String>>of(String::toLowerCase, String::toUpperCase)
        .map(f->f.apply(s)))
    .forEach(System.out::println);
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!