My question is about InterruptedException
, which is thrown from the Thread.sleep
method. While working with ExecutorService
I noticed some
The reason for this, is that these invocations are in fact, invocations to two different overloaded methods available in ExecutorService
; each of these methods taking a single argument of different types:
Future submit(Callable task); Future> submit(Runnable task);
Then what happens is that the compiler is converting the lambda in the first case of your problem into a Callable>
functional interface (invoking the first overloaded method); and in the second case of your problem converts the lambda into a Runnable
functional interface (invoking therefore the second overloaded method), requiring because of this to handle the Exception
thrown; but not in the previous case using the Callable
.
Although both functional interfaces don't take any arguments, Callable>
returns a value:
- Callable:
V call() throws Exception;
- Runnable:
public abstract void run();
If we switch to examples that trim the code to the relevant pieces (to easily investigate just the curious bits) then we can write, equivalently to the original examples:
ExecutorService executor = Executors.newSingleThreadExecutor();
// LAMBDA COMPILED INTO A 'Callable>'
executor.submit(() -> {
while (true)
throw new Exception();
});
// LAMBDA COMPILED INTO A 'Runnable': EXCEPTIONS MUST BE HANDLED BY LAMBDA ITSELF!
executor.submit(() -> {
boolean value = true;
while (value)
throw new Exception();
});
With these examples, it may be easier to observe that the reason why the first one is converted to a Callable>
, while the second one is converted to a Runnable
is because of compiler inferences.
In both cases, the lambda bodies are void-compatible, since every return statement in the block has the form return;
.
Now, in the first case, the compiler does the following:
throw new ()
.complete normally
and therefore is value-compatible.Callable>
and Runnable
are potential matches for this lambda, the compiler selects the most specific match (to cover all scenarios); which is the Callable>
, converting the lambda into an instance of it and creating an invocation reference to the submit(Callable>)
overloaded method.While, in the second case, the compiler does the following:
complete normally
.Runnable
(as it is the only available fitting functional interface for the lambda to be converted into) and creates an invocation reference to the submit(Runnable)
overloaded method. All this coming at the price of delegating to the user, the responsibility of handling any Exception
s thrown wherever they MAY occur within portions of the lambda body.This was a great question - I had a lot of fun chasing it down, thanks!