Convert Runnable to Supplier
Asked Answered
K

3

7

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.

Khotan answered 10/2, 2019 at 15:39 Comment(9)
run method returns void, so you can't supply any value.Myogenic
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.Khotan
Consider using Callable which returns a value, instead of Runnable.Cartwheel
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).Maybe
@Maybe I am catching (specific) RuntimeExceptions 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.Khotan
() -> { runnable.run(); return null; } does not create more objects than runnable::run would.Lonilonier
@Holger, since the lambda is capturing (captures the runnable) it likely cannot be reused, see stackoverflow.com/a/28466374Khotan
So does runnable::run. No difference.Lonilonier
Right, nevermind, I will update the question accordingly. ThanksKhotan
T
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.

Transpacific answered 10/2, 2019 at 15:51 Comment(0)
P
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.

Photocomposition answered 10/2, 2019 at 16:32 Comment(5)
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.Khotan
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.Khotan
@Khotan 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?Photocomposition
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.Khotan
@Khotan 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.Photocomposition
D
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>.

Defelice answered 8/7, 2021 at 19:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.