Search code examples
javarunnablecallable

How to know if a Future is created with Callable or Runnable


I am dipping my feet in Futures. A Future can be created with a Runnable and with a Callable. Is there a way to decide how it was created?

For example, I have the following code:

        Future<?>       future  = null;
        Future<?>       future2 = null;
        ExecutorService service = null;
        service = Executors.newSingleThreadExecutor();
        future = service.submit(() -> {
                for (int i = 0; i < 5; ++i) {
                    System.out.println("Printing record: " + i);
                    Thread.sleep(5);
                }
                return "Done";
            });
        future2 = service.submit(() -> System.out.println("Printing zoo inventory"));
            System.out.println("================================================================");
        System.out.println(future);
        System.out.println(future.get().getClass());
        System.out.println(future.get());
        System.out.println("================================================================");
        System.out.println(future2);
        try {
            System.out.println(future2.get().getClass());
            System.out.println(future2.get());
        } catch (ExecutionException e) {
            System.out.println("Could not do a get");
        }
        System.out.println("================================================================");

This results in ending with:

================================================================
java.util.concurrent.FutureTask@5caf905d[Completed normally]
class java.lang.String
Done
================================================================
java.util.concurrent.FutureTask@1f32e575[Completed normally]
Exception in thread "main" java.lang.NullPointerException
        at ZooInfo.main(ZooInfo.java:56)

I could solve this by using:

                    if (future2.get() == null) {
                        System.out.println("Made with a Runnable");
                    } else {
                        System.out.println(future2.get().getClass());
                        System.out.println(future2.get());
                    }

The problem with this is that when the Runnable still takes some time, I am waiting on the get for nothing. Is there a way to determine if a Future was created with a Runnable, or a Callable without resorting to using get()?


Solution

  • I don't believe that you really need to know whether the Future was created from a Runnable or a Callable.

    For one thing, there are more ways than that to create a Future: for example, CompleteableFuture is not created from either; and, more generally, since Future is an interface, one can create instances however you like.

    For another: the abstraction of Future is that it is something that gives you a (possibly null) value when it completes, or throws an exception. That's all it is meant to do.

    (Also, your current approach of checking for nullity of the return value doesn't work reliably because Callable.call() is allowed to return null).

    If you need it to do something else, you may want to revisit your design so you can simply treat it as it is intended.


    But if you really do have a use case that does require you to know how it was created, you need to control the creation. Rather than letting callers submit code directly to the executor, wrap in a class like this:

    class YourExecutor {
      // Initialize in ctor.
      private final ExecutorService executor;
    
      FromRunnable submit(Runnable r) {
        return new FromRunnable(executor.submit(r));
      }
    
      <T> FromCallable<T> submit(Callable<? extends T> c) {
      return new FromCallable<>(executor.submit(c));
      }
    }
    

    where FromRunnable and FromCallable<T> are classes implementing Future<Void> and Future<T> respectively, which delegate all of the methods to another instance of a compatible Future (passed as the constructor parameter).

    You can then check the provenance using instanceof; or by some other means, such as extending a common base case or interface which provides a method describing the provenance.

    But, just to reiterate, a better approach is to design your code so it doesn't need to know.