Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert Consumer into Runnable inside Stream.map()

I am trying to convert a Consumer to a Runnable. The following code does not generate any compiler errors in Eclipse IDE.

Consumer<Object> consumer;
Runnable runnable;
Object value;

...
runnable = () -> consumer.accept(value);

The following code generates a compiler error in Eclipse IDE.

ArrayList<Consumer<Object>> list;
Object value;

...

list.
   stream().
   map(consumer -> () -> consumer.accept(value));

The errors are:

Type mismatch: Can not convert from Stream<Object> to <unknown>.
The target type of this expression must be a functional interface.

How do I help the compiler convert a Consumer to a Runnable?

The following code fixes the problem but is very verbose.

map(consumer -> (Runnable) (() -> consumer.accept(value)));

Is there a more concise way to do this? I know I could create a static method which accepts a Consumer and returns a Runnable, but I don't think of that as more concise.

like image 827
Nathan Avatar asked Feb 18 '16 18:02

Nathan


1 Answers

The error message is normal if you consider the expression:

list.stream().map(consumer -> () -> consumer.accept(value))
                              ^--------------------------^
                                what is the type of that?

The problem is that the compiler has no way of determining the target type of the expression () -> consumer.accept(value). It certainly can be Runnable, but it could also be MyAwesomeInterface declared with:

@FunctionalInterface
interface MyAwesomeInterface { void foo(); }

In fact, it can comply to any functional interface that declares a functional method taking no parameter and returning no value. As such, this results in a compilation error.

There is no error when you explicitly store the lambda expression in a Runnable with:

Runnable runnable = () -> consumer.accept(value);

because, then the compiler knows that the target type of that lambda is Runnable.


The problem is more obscure when you consider:

List<Runnable> runnables = list.stream()
                               .map(consumer -> () -> consumer.accept(value))
                               .collect(Collectors.toList());

One could argue that the compiler may be able to infer the target type of that expression to be Runnable since we're collecting that into a list of Runnable. But, it doesn't and you have to help the compiler out a little and explicitly tell the compiler that the Stream elements are Runnable:

List<Runnable> runnables = list.stream()
                               .<Runnable> map(consumer -> () -> consumer.accept(value))
                               .collect(Collectors.toList());
like image 194
Tunaki Avatar answered Oct 09 '22 23:10

Tunaki