javagenericstype-inferencejls

Type Inference resolution procedure involving throws clause in Java


Consider the following article in JLS §18.1.3 - Bounds

Here when we try to identify the set of bounds on the inference variables - we have one of the following situations:

...

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

...

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.

I think this statement is incorrect:

Is my understanding correct here or am I missing something?


Solution

  • I think your interpretation/understanding of this statement is slightly misguided:

    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.

    That line is referring to the resolution, which, as I understand it, is not about where throws α is, but about where α is inferred, conceivably the invocation of the method.

    Consider this class:

    static class MyClass {
    
        public static void main(String[] args) {
            MyClass.<RuntimeException>something(0); // same as MyClass.something(1);
    
            try {
                MyClass.<IOException>something(2);
            } catch (IOException ex) {
                // checked exception
            }
        }
    
        /**
         * Will throw IOException if argument is 2, a RuntimeException otherwise
         */
        static <T extends Exception> void something(int a) throws T {
            if (a == 2) {
                throw (T) new IOException(); //of course it's a bad cast
            } else {
                throw (T) new Exception();
            }
        }
    }
    

    After analyzing the two something method, focus on the invocation in the main method:

    The call MyClass.<IOException>something(0) expects an IOException. The caller knows it (assume fully documented contract rather than tightly coupled code), and handles the exception.
    This already tells you that the variable can be a checked exception, contrary to what you think.

    On the contrary, the call MyClass.<RuntimeException>something(0) expects a runtime exception on similar grounds.

    How α (T in the above example) is inferred allows the compiler to skip forcing the caller to catch/handle the exception (if it's to look at the bound, which it'd otherwise have to)

    Now about the "optimization": The type variable being bounded as extends Exception can reasonably be expected to resolve to a checked exception. But, if the caller knows that it shall be a runtime exception, it can "inform" the compiler that it's going to be a runtime exception. This is what I did by specifying RuntimeException in the type witness (RuntimeException is also the resolved type when no type argument is given explicitly).

    We can spend days to interpret "optimization", but at least I as a caller did not have to try/catch the invocation, and I still didn't upset the compiler (first invocation).