Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference between raw and parameterized Optional when invoking orElseGet(Supplier)

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:

    • Line 4 and 6: Unhandled exception: java.lang.Exception
    • Line 5: Unhandled exception: java.lang.Throwable This makes me feel that line 4 is called with type parameter as <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?

like image 606
Sy Pham Avatar asked Oct 27 '25 06:10

Sy Pham


1 Answers

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;

like image 136
marcinj Avatar answered Oct 28 '25 20:10

marcinj



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!