6

How can a Runnable be converted to a Supplier?

public <T> T useSupplier(Supplier<T> supplier) {
    // Does something with supplier and returns supplied value
    ...
    return value;
}

public void useRunnable(Runnable runnable) {
    // Somehow convert runnable to Supplier
    ...
    useSupplier(supplier);
}

Here I would like to reuse the method useSupplier for useRunnable, for example because I do not want to duplicate the code. The behavior of useSupplier does not matter for this question, let's say it wraps thrown exceptions, or uses the supplier in a synchronized block.


Edit: To clarify, the method useSupplier does not interact with the supplied value, it just returns it. The functionality of useSupplier is to retrieve the value from the supplier in some context, in my case it catches (specific) RuntimeExceptions, creates a new exception with it as cause and throws it:

public <T> T useSupplier(Supplier<T> supplier) {
    try {
        return supplier.get();
    }
    catch (RuntimeException runtimeException) {
        throw new MyException("Supplier threw exception", runtimeException);
    }
}

The following solutions do not work (in Java 8):

useSupplier(runnable);
useSupplier(runnable::run);
useSupplier((Supplier<Void>) runnable::run);

One solution I could come up with is creating a new Supplier which returns an arbitrary value:

useSupplier(() -> {
    runnable.run();
    return null;
});

Is there a smaller solution?

Edit: As pointed out by Holger in the comments, using runnable::run will also create new lambda instances since it is stateful, see also this answer.

Marcono1234
  • 5,856
  • 1
  • 25
  • 43
  • `run` method returns void, so you can't supply any value. – SMA Feb 10 '19 at 15:41
  • For `useRunnable` I do not care about the returned value. As I wrote, `useSupplier` uses for example the supplier in a synchronized block and this is what I want to have for `useRunnable` as well. – Marcono1234 Feb 10 '19 at 15:43
  • Consider using `Callable` which returns a value, instead of `Runnable`. – c0der Feb 10 '19 at 15:52
  • Can you tell us a bit more about the semantics of `useRunnable(...)` and `useSupplier(...)`? From the naming, it transports that `useRunnable(...)` spawns a new threadt (at least for me). – Turing85 Feb 10 '19 at 15:57
  • @Turing85 I am catching (specific) `RuntimeException`s and throwing new exceptions with them as cause. So I do not care about what value the supplier returned, I only return it as well. – Marcono1234 Feb 10 '19 at 16:02
  • `() -> { runnable.run(); return null; }` does not create more objects than `runnable::run` would. – Holger Feb 10 '19 at 18:02
  • @Holger, since the lambda is capturing (captures the runnable) it likely cannot be reused, see https://stackoverflow.com/a/28466374 – Marcono1234 Feb 10 '19 at 18:11
  • So does `runnable::run`. No difference. – Holger Feb 10 '19 at 18:12
  • Right, nevermind, I will update the question accordingly. Thanks – Marcono1234 Feb 10 '19 at 18:14

3 Answers3

8

In your case you cannot avoid creating new object. Even if there is a method somewhere that converts a Runnable to a Supplier, it will create an object there. So your solution is valid, you won't find any better.

Pay attention to that Supplier is expected to provide values and Runnable just represents an action. They are used for different purposes. So your need of converting Runnable to Supplier may be a result of a design problem involved.

0

Looking at your design, you might just be looking for Consumer which accepts a type and just processes(consumes) it without return a value and can be used to adapt a runnable as well, instead of a Supplier which on the other hand is expected to return (supplies) a value post-processing.

You could use something like :

private static <T> void useConsumer(Consumer<T> consumer, T val) {
    // Does something with supplier and returns supplied value
    consumer.accept(val);
}

public static <T> void useRunnable(Runnable runnable) {
    useConsumer(Runnable::run, runnable);
}

If the requirement is to use the Supplier for sure, then you can invoke that method as :

public static void useRunnable(Runnable runnable) {
    useSupplier(() -> runnable); // the useSupplier returns the 'runnable' when this method is called
}

As mentioned in the comments, now when you invoke useRunnable, the useSupplier would return the same runnable, but the method useRunnable is void again and hence its ignored altogether.

Naman
  • 27,789
  • 26
  • 218
  • 353
  • I want to have a method with `Supplier` as argument. I edited my question now to hopefully make it clearer that I do not want to process the supplied value. – Marcono1234 Feb 10 '19 at 17:19
  • No, the runnable should run. This way (with the edit I made to my question) the supplier would supply the runnable which would just be discarded. – Marcono1234 Feb 10 '19 at 18:02
  • 1
    @Marcono1234 *the supplier would supply the runnable which would just be discarded*, then why use `Supplier` even? that's why I initially suggested using `Consumer` instead. Do note, your implementation `() -> { runnable.run(); return null; }` does *`return`* something, be it `null`, but then it does not have anything to do it with `runnable`. Or does it? – Naman Feb 10 '19 at 18:05
  • See my edit to the question. When I use a supplier I am interested in the value but want all exceptions to be handled in a certain way. When I use a runnable I also want all exceptions to be handled in a certain way. – Marcono1234 Feb 10 '19 at 18:08
  • 2
    @Marcono1234 Then I doubt there is nothing better than `() -> { runnable.run(); return null; }` to call it with. When you just don't care of the `return` type of `useSupplier` and are primarily interested to deal with exceptions from `runnable` executed. – Naman Feb 10 '19 at 18:10
0

If you find yourself using this pattern a lot in your codebase, it might be worth creating a RunnableSupplier class:

public class RunnableSupplier<T> implements Supplier<T> {
    private final Runnable runnable;

    public RunnableSupplier(Runnable runnable) {
        this.runnable = runnable;
    }

    @Override
    public T get() {
        runnable.run();
        return null;
    }
}
public void useRunnable(Runnable runnable) {
    useSupplier(new RunnableSupplier(runnable));
}

I've made this class generic since null can be returned for suppliers of all generic types. This makes it possible to use in library methods that require Suppliers of a specific type, so long as they allow null results. If you want to enforce that it's always a Supplier<Void>, it's straightforward to make it non-generic and implement Supplier<Void> instead of Supplier<T>.

M. Justin
  • 14,487
  • 7
  • 91
  • 130