While writing code for another answer on this site I came across this peculiarity:
static void testSneaky() { final Exception e = new Exception(); sneakyThrow(e); //no problems here nonSneakyThrow(e); //ERRROR: Unhandled exception: java.lang.Exception } @SuppressWarnings("unchecked") static <T extends Throwable> void sneakyThrow(Throwable t) throws T { throw (T) t; } static <T extends Throwable> void nonSneakyThrow(T t) throws T { throw t; }
First, I am quite confused why the sneakyThrow
call is OK to the compiler. What possible type did it infer for T
when there is no mention anywhere of an unchecked exception type?
Second, accepting that this works, why then does the compiler complain on the nonSneakyThrow
call? They seem very much alike.
Type inference is a Java compiler's ability to look at each method invocation and corresponding declaration to determine the type argument (or arguments) that make the invocation applicable.
In some cases type inference can be easier to read and understand because it is simpler than explicit type specification.
The T of sneakyThrow
is inferred to be RuntimeException
. This can be followed from the langauge spec on type inference (http://docs.oracle.com/javase/specs/jls/se8/html/jls-18.html)
Firstly, there's a note in section 18.1.3:
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.
This doesn't affect anything, but it points us to the Resolution section (18.4), which has got more information on inferred exception types with a special case:
... Otherwise, if the bound set contains
throws αi
, and the proper upper bounds of αi are, at most,Exception
,Throwable
, andObject
, then Ti =RuntimeException
.
This case applies to sneakyThrow
- the only upper bound is Throwable
, so T
is inferred to be RuntimeException
as per the spec, so it compiles. The body of the method is immaterial - the unchecked cast succeeds at runtime because it doesn't actually happen, leaving a method that can defeat the compile-time checked exception system.
nonSneakyThrow
does not compile as that method's T
has got a lower bound of Exception
(ie T
must be a supertype of Exception
, or Exception
itself), which is a checked exception, due to the type it's being called with, so that T
gets inferred as Exception
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With