0

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

Cecil
  • 41
  • 7
  • "The problem with this is that" a `Callable` can also return null. – Andy Turner Jul 15 '19 at 15:30
  • Why? A Runnable and a Callable behave the same way. `getClass()` on `null` returned by the `get` however is a bad idea. – luk2302 Jul 15 '19 at 15:31
  • 2
    Is there a practical use for this question? Normally, when you write a system that relies on futures, you know what you sent and you `get` or don't `get` accordingly. You don't arbitrarily pour tasks you don't know about into your executor service. – RealSkeptic Jul 15 '19 at 15:35
  • 1
    They are indistinguishable, even with `get`, because you can return `null` in a `Callable` as well. – Sweeper Jul 15 '19 at 15:36
  • @RealSkeptic Well, personally I would use different variables, but when you are working in bigger organisations you never know what another person will do. (Or has done.) I always like to know a little bit more, so when I get in a situation like that, I do not have to do research for this -because often it has to be solved immediately- so I can solve it quickly – Cecil Jul 15 '19 at 15:41
  • 1
    In an organization, you *plan* your APIs. You don't just get random tasks from unknown sources. The module you write has to have some meaning, defined inputs and defined outputs, especially when you work in a strictly typed language like Java. Your `Future` will not have a `?` as its type. Anyway, your situation boils down to having to do a null check before using any method on your result - because when you get unknown results, some of them can be null. – RealSkeptic Jul 15 '19 at 15:53
  • Use if (object instanceof Callable) – Joe Jul 15 '19 at 16:50
  • @RealSkeptic I am afraid that what you describe is what should be done, but often not is done. The other problem is that sometimes later on the way things are handled changes. And maybe I am to negative, but I prefer to be prepared for nothing, as the other way around. – Cecil Jul 17 '19 at 06:59

1 Answers1

2

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.

Andy Turner
  • 137,514
  • 11
  • 162
  • 243
  • Well, I am just learning this stuff, so it is possible that I ask questions that seems strange to someone who is experienced with Futures. :-D I agree that a good design should circumvent things like this. But I have seen to often that the spur of the moment is more important as the long term. Also it could be that first it is not seen as important and later it is. New insights and things like that. – Cecil Jul 17 '19 at 07:05