dr0i
dr0i

Reputation: 2470

Multiple lambda method references

It's possible to chain/concatenate what is done with elements in a lambda expression like this:

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

Is there a way to do this also with method references? Something like this:

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

I know I could do this in four separate calls (which do also more, that is mutate the values):

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

I can't even do something easy like this:

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

Upvotes: 6

Views: 7431

Answers (3)

Holger
Holger

Reputation: 298153

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

Upvotes: 3

the8472
the8472

Reputation: 43052

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
}

Upvotes: 9

sprinter
sprinter

Reputation: 27956

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.

Upvotes: 3

Related Questions