javagenericslambdatype-inferencejls

Resolving the constraint on Exception during type inference


Consider the following type of constraint: 18.1.3. Bounds

throws α: The inference variable α appears in a throws clause.

JLS says that:

A bound of the form throws α is purely informational: it directs resolution to optimize the instantiation of α so that, if possible, it is not a checked exception type.

Now when it is said that "optimize the instantiation of α so that, if possible, it is not a checked exception type" - Not sure why it says that is optimized to be a non-checked type exception type? It should rather be a "checked" type as that is what being deliberate in the declaration.


Solution

  • The case that this clause is talking about is situations like this:

    public class SomeClass {
        public static void main(String[] args) {
            f();
        }
    
        public static <T extends Exception> void f() throws T {}
    }
    

    At the callsite f();, the type variable T has both an Exception bound and also a throws bound. According to the spec, since it is possible for T to be instantiated as a non-checked exception type, it is instantiated as a non-checked exception type, namely RuntimeException in this case

    As a result, the code above does not give any "unhandled exception" compile-time errors, which hopefully is what you expect. If the spec hadn't included the sentence about "if possible, it is not a checked exception type", T would have been instantiated as Exception and I would have needed to handle it at the call site.

    On the other hand, if it is not possible to infer a non-checked exception type for T, then inference process continues as usual. Inference will not fail because of the existence of a throws bound, hence the phrases "purely informational" and "if possible".

    public static void main(String[] args) throws Exception {
        f(new Exception());
    }
    
    public static <T extends Exception> void f(T t) throws T { }
    

    The idea is that, for a method that can throw any type of exception (both checked and unchecked), it shouldn't throw a checked exception unless it is impossible to throw an unchecked one. e.g. if the caller is specifying the type arguments explicitly, or is passing in other arguments that make it impossible for T to be an unchecked exception. In short, unchecked is the default.

    It might also be helpful to compare the case of having the throws bound vs not having it:

    public static void main(String[] args) {
        var x = f();
        var y = g();
    }
    
    public static <T extends Exception> T f() throws T { return null; }
    public static <T extends Exception> T g() { return null; }
    

    The type of x is inferred to be RuntimeException, whereas the type of y is inferred to be Exception.