Consider the following code:
public class Main {
public static void main(String[] args)
// throws Exception // line 1
{
Optional empty1 = Optional.empty(); // line 2
Optional<Object> empty2 = Optional.empty(); // line 3
Optional.empty().orElseThrow(() -> new Exception("Empty optional")); // line 4
empty1.orElseThrow(() -> new Exception("Empty optional")); // line 5
empty2.orElseThrow(() -> new Exception("Empty optional")); // line 6
}
}
When line 1 is commented, then line 4, 5, 6 all report compile-time errors:
<Object>
like line 6, but what makes the differences on the exact exception thrown?When line 1 is uncommented, then only line 5 reports compile-time error: Unhandled exception: java.lang.Throwable. It will be solved if I change line 1 to throws Throwable. Why throwing Exception is not enough for line 5 to resolve compile-time error?
The difference is due to the use of raw types in Java. Method signature of orElseThrow is:
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X
So, for case in line 2 and 5 you have:
Optional empty1 = Optional.empty(); // line 2
empty1.orElseThrow(() -> new Exception("Empty optional")); // line 5
in above orElseThrow
signature case compiler is not provided with T
type and even thought it has X
, it will generate raw call. It will substitute X with its upperbound which is Throwable, and T will be Object. The result is:
public Object orElseThrow(Supplier exceptionSupplier) throws Throwable
^^^^^^^^^
thus, compiler reports:
error: unreported exception Throwable
The case of:
Optional.empty().orElseThrow(() -> new Exception("Empty optional")); // line 4
is more interesting, here no explicit type is specified, but compiler reports error of Exception
being thrown. So its different from the previous case. You again must look at the signature of the used function, in this case Optional.empty():
public static<T> Optional<T> empty()
^^^ ~~~ type inference
The important part is <T>
which indicate that it will infer its type T
from the enclosing context. In line 4
compiler is unable to infer type, and uses Object
type instead. I believe in JLS its stated here:
https://docs.oracle.com/javase/specs/jls/se10/html/jls-18.html#jls-18.1.3
If Pl has no TypeBound, the bound αl <: Object appears in the set.
and also in oracle tutorial:
https://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html
The compiler requires a value for the type argument T so it starts with the value Object. Consequently, the invocation of Collections.emptyList returns a value of type List<Object>
The important thing, is that in line 4
you are not dealing with raw types like in lines 2 and 5, but with generic ones (generic type T
is Object
), so when orElseThrow is called, its signature is
public Object orElseThrow(Supplier exceptionSupplier) throws Exception
both generic types T
nad X
are known, so generic function call is generated, which in the end causes in your example this compiler error:
error: unreported exception Exception;
Now, line 3 and 6, is explicitly using generic type Object, and this makes the signature of orElseThrow as previously:
public Object orElseThrow(Supplier exceptionSupplier) throws Exception
which gives the same error:
error: unreported exception Exception;