Nathan
Nathan

Reputation: 8981

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.

Upvotes: 7

Views: 4444

Answers (1)

Tunaki
Tunaki

Reputation: 137329

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

Upvotes: 14

Related Questions