12

I've been switching over to Java from C# after some recommendations from some over at CodeReview. So, when I was looking into LWJGL, one thing I remembered was that every call to Display must be executed on the same thread that the Display.create() method was invoked on. Remembering this, I whipped up a class that looks a bit like this.

public class LwjglDisplayWindow implements DisplayWindow {
    private final static int TargetFramesPerSecond = 60;
    private final Scheduler _scheduler;

    public LwjglDisplayWindow(Scheduler displayScheduler, DisplayMode displayMode) throws LWJGLException {
        _scheduler = displayScheduler;
        Display.setDisplayMode(displayMode);
        Display.create();
    }

    public void dispose() {
        Display.destroy();
    }

    @Override
    public int getTargetFramesPerSecond() { return TargetFramesPerSecond; }

    @Override
    public Future<Boolean> isClosed() {
        return _scheduler.schedule(() -> Display.isCloseRequested());
    }
}

While writing this class you'll notice that I created a method called isClosed() that returns a Future<Boolean>. This dispatches a function to my Scheduler interface (which is nothing more than a wrapper around an ScheduledExecutorService. While writing the schedule method on the Scheduler I noticed that I could either use a Supplier<T> argument or a Callable<T> argument to represent the function that is passed in. ScheduledExecutorService didn't contain an override for Supplier<T> but I noticed that the lambda expression () -> Display.isCloseRequested() is actually type compatible with both Callable<bool> and Supplier<bool>.

My question is, is there a difference between those two, semantically or otherwise - and if so, what is it, so I can adhere to it?

  • I was under the impression code that doesn't work = SO, code that works but needs review = CodeReview, general questions that may or may not need code = programmers. My code does actually work and is only there as an example. I'm not asking for a review, either, just asking about semantics. – Dan Pantry Aug 24 '14 at 17:30
  • ..asking about the semantics of something is not a conceptual question? – Dan Pantry Aug 24 '14 at 17:36
  • I think this is a conceptual question, not as conceptual as other good questions on this site but it is not about implementation. The code works, the question is not about the code. The question is asking "what is the difference between these two interfaces?" – user22815 Aug 24 '14 at 18:14
  • Why would you want to switch from C# to Java! – Didier A. Feb 3 '16 at 19:24
  • 2
    There is one difference, namely that Callable.call() throws exceptions and Supplier.get() doesn't. That makes the latter much more attractive in lambda expressions. – Thorbjørn Ravn Andersen Aug 23 '16 at 8:02
6

The short answer is that both are using functional interfaces, but it's also worthy to note that not all functional interfaces must have the @FunctionalInterface annotation. The critical part of the JavaDoc reads:

However, the compiler will treat any interface meeting the definition of a functional interface as a functional interface regardless of whether or not a FunctionalInterface annotation is present on the interface declaration.

And the simplest definition of a functional interface is (simply, without other exclusions) just:

Conceptually, a functional interface has exactly one abstract method.

Therefore, in @Maciej Chalapuk's answer, it is also possible to drop the annotation and specify the desired lambda:

// interface
public interface MyInterface {
    boolean myCall(int arg);
}

// method call
public boolean invokeMyCall(MyInterface arg) {
    return arg.myCall(0);
}

// usage
instance.invokeMyCall(a -> a != 0); // returns true if the argument supplied is not 0

Now, what makes both Callable and Supplier functional interfaces is because they do contain exactly one abstract method:

  • Callable.call()
  • Supplier.get()

Since both methods do not take in an argument (as opposed to the MyInterface.myCall(int) example), the formal parameters are empty (()).

I noticed that the lambda expression () -> Display.isCloseRequested() is actually type compatible with both Callable<Boolean> and Supplier<Boolean>.

As you should be able to infer by now, that is just because both abstract methods will return the type of the expression you use. You should definitely be using a Callable given your usage of a ScheduledExecutorService.

Further exploration (beyond the scope of the question)

Both interfaces comes from entirely different packages, hence they are used differently too. In your case, I don't see how an implementation of Supplier<T> will be used, unless it is supplying a Callable:

public static <T> Supplier<Callable<T>> getCallable(T value) {
    return () -> () -> {
        return value;
    };
}

The first () -> can be loosely interpreted as "a Supplier gives..." and the second as "a Callable gives...". return value; is the body of the Callable lambda, which itself is the body of the Supplier lambda.

However, usage in this contrived example gets slightly complicated, as you now need to get() from the Supplier first before get()-ting your result from the Future, which will in turn call() your Callable asynchronously.

public static <T> T doWork(Supplier<Callable<T>> callableSupplier) {
    // service being an instance of ExecutorService
    return service.submit(callableSupplier.get()).get();
}
  • 1
    I'm switching accepted answer to this answer because this is just simply far more comprehensive – Dan Pantry May 10 '15 at 12:01
19

One basic difference between the 2 interfaces is that Callable allows checked exceptions to be thrown from within the implementation of it, while Supplier doesn't.

Here are the code snippets from the JDK highlighting this -

@FunctionalInterface
public interface Callable<V> {
/**
 * Computes a result, or throws an exception if unable to do so.
 *
 * @return computed result
 * @throws Exception if unable to compute a result
 */
V call() throws Exception;
}

@FunctionalInterface
public interface Supplier<T> {

/**
 * Gets a result.
 *
 * @return a result
 */
T get();
}
  • This makes Callable unusable as argument in functional interfaces. – Basilevs Jun 28 '16 at 9:18
  • 3
    @Basilevs no it doesn't - it's just not usable in places that expect a Supplier, such as the stream API. You absolutely can pass lambdas and method references to methods that take a Callable. – dimo414 Mar 10 '18 at 2:10
10

As you note, in practice they do the same thing (provide some sort of value), however in principle they are intended to do different things:

A Callable is "A task that returns a result, while a Supplier is "a supplier of results". In other words a Callable is a way to reference a yet-unrun unit of work, while a Supplier is a way to reference a yet-unknown value.

It's possible that a Callable could do very little work and simply return a value. It's also possible a Supplier could do quite a lot of work (e.g. construct a large data structure). But generally speaking what you care about with either is their principle purpose. For example an ExecutorService works with Callables, because it's primary purpose is to execute units of work. A lazy-loaded data store would use a Supplier, because it cares about being supplied a value, without much concern about how much work that might take.

Another way of phrasing the distinction is that a Callable may have side-effects (e.g. writing to a file), while a Supplier should generally be side-effect free. The documentation doesn't explicitly mention this (since it's not a requirement), but I'd suggest thinking in those terms. If the work is idempotent use a Supplier, if not use a Callable.

2

Both are normal Java interfaces with no special semantics. Callable is a part of concurrent API. Supplier is a part of new functional programming API. They can be created from lambda expressions thanks to changes in Java8. @FunctionalInterface makes the compiler check that the interface is functional and raise an error if it isn't, but an interface doesn't need that annotation to be a functional interface and be implemented by lambdas. This is like how a method can be an override without being marked @Override but not vice-versa.

You can define your own interfaces compatible with lambdas and document them with @FunctionalInterface annotation. Documenting is optional though.

@FunctionalInterface
public interface MyInterface {
    boolean myCall(int arg);
}

...

MyInterface var = (int a) -> a != 0;
  • Although worth noting this particular interface is called IntPredicate in Java. – Konrad Borowski Feb 28 '18 at 9:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.