Callable and Supplier
– never shall the two meet!?

Let’s say we have this clever little legacy API method:

Now, we discover Java 8’s neat Supplier functional interface. As a courtesy to our users, we decide we would like to accept those as well. Does this work?
A naïve addition gives us:

So far so good? Well, what happens if we try to call this?

At least our old code still works:

Of course. A Callable is not a Supplier, so no issues there since Java 8 still handles method overloading 🙂
So, our old users are still happy. How about the new ones? We should be able to just toss in a λ expression, right?
Let’s try:

Wait, what? We get error: reference to run is ambiguous! Why???

The answer is that the compiler is looking for a functional interface here that matches the λ expression supplied, to see which run method to invoke (since we so cleverly have two).
Problem is, both Callable and Supplier has the same type of functional interface: they accept no parameters and return an object. Oops.

So, what do we do? The solution is actually quite simple: we get rid of the second method again!

Wait, what? How can we then supply a λ expression? Wouldn’t it then look for a method accepting a Supplier and not find one?

No, there is really no problem. The compiler automatically figures out that the λ should be a Callable instead of the normal Supplier!

Just deleted it again, and now it works! Yay!

Well, not so fast 🙂 What do we do if we already have a Supplier object, maybe from a parameter?

Something like Supplier<String> supplier = () -> "supplied" maybe, this will surely not match a Callable parameter?

Indeed, it does not. The good thing is that the solution is super simple™:

The compiler automatically figures out that supplier::get has to match a Callable, so it makes one for you!

Wow, magic! Just tried, and yay, it works!

Well, not so fast 🙂 How about the other way around?

OK, let’s say we have a Callable object, and we don’t have a legacy run(Callable<T> c) method, and we want to have a new, shiny run(Supplier<T> s) method, and, and…

Yes, here comes the slightly tricky bit™.
We try this:

No, this does not compile.
We get method run in class Caller cannot be applied to given types. Great.

Ah, I know a trick! We just add ::call!
And by the way we can remove that ::get on line 20 too, right?

Well, not so fast 🙂 Getting rid of ::get is right (although not strictly necessary), but adding ::call will not help. The problem is that Callable’s call() method throws Exception, while Supplier’s get() method does not, so the compiler doesn’t know what to do with that potential exception it suddenly has to deal with.
Luckily, the solution is simple, and in hindsight a bit too obvious: we have to tell the compiler what to do with it!
The easiest way to do that is of course to toss together a Supplier that gives us what the callable does, but hides the exception:

This will work as it should.

Lasciate ogni speranza, voi ch’entrate.

But, bleh, this is sort of ugly, although the old Callable is of course what stains our beautiful Java 8 here.
Is there no other way?

Well, actually there is:

What this fantastic nonsense does and why it works is trivial and is left as an exercise for the reader to figure out. 🙂

As always, should you be caught doing this, I will disavow any knowledge of your actions.

Leave a Reply

Your email address will not be published. Required fields are marked *

Please answer this amazingly complicated math question to prove that you are not a spam bot: