Specificity of exception type inference in Java 8

When writing code for another answer on this site, I came across this feature:

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; } 

Firstly, I'm rather confused why calling sneakyThrow is suitable for the compiler. What possible type did he make for T when there is no mention of an unlimited type of exceptions?

Secondly, recognizing that this works, why does the compiler complain about calling nonSneakyThrow ? They seem very similar.

+75
java generics java-8 type-inference
Jul 09 '15 at 11:49
source share
3 answers

The value of T sneakyThrow is defined as a RuntimeException . This can be done from the langauge spec on output type ( http://docs.oracle.com/javase/specs/jls/se8/html/jls-18.html )

Firstly, there is a note in section 18.1.3:

The evaluation of the form throws Ξ± is purely informative: it allows the optimization of the instance Ξ± to be allowed so that, if possible, this is not a tested type of exception.

This does not affect anything, but points to the permission section (18.4), which contains additional information about the alleged types of exceptions with a special case:

... Otherwise, if the connected set contains throws Ξ±i , and the proper upper bounds Ξ±i are at most Exception , Throwable and Object , then Ti = RuntimeException .

This case refers to sneakyThrow - the only upper bound is Throwable , therefore T is defined as a RuntimeException according to the specification, therefore it compiles. The body of the method does not matter - uncontrolled execution is performed at run time because it does not actually occur, leaving a method that can defeat the exception system, which depends on compilation time.

nonSneakyThrow does not compile since this method T received the lower bound of Exception (i.e., T must be a supertype of Exception or Exception ), which is a checked exception, due to the type with which it is called, so T is defined as Exception .

+55
Jul 09 '15 at 12:01
source share

If type inference creates a single upper bound for a type variable, usually the upper bound is chosen as the solution. For example, if T<<Number , the solution is T=Number . Although Integer , Float , etc. They can also satisfy the restriction; there is no good reason to choose them by Number .

This was also the case with throws T in java 5-7: T<<Throwable => T=Throwable . (Sneaky throw solutions all had explicit arguments of type <RuntimeException> , otherwise <Throwable> .)

In java8 with the introduction of lambda, this becomes problematic. Consider this case

 interface Action<T extends Throwable> { void doIt() throws T; } <T extends Throwable> void invoke(Action<T> action) throws T { action.doIt(); // throws T } 

If we refer to an empty lambda, what would T output as?

  invoke( ()->{} ); 

The only limit on T is the upper bound of Throwable . In the early stages of java8, T=Throwable would be inferred. See This Report I Submitted.

But it's pretty stupid to Throwable , an exception thrown from an empty block. The report suggested a solution (which, apparently, was taken by JLS) -

 If E has not been inferred from previous steps, and E is in the throw clause, and E has an upper constraint E<<X, if X:>RuntimeException, infer E=RuntimeException otherwise, infer E=X. (X is an Error or a checked exception) 

i.e. if the upper bound is Exception or Throwable , select RuntimeException as the solution. In this case, there is good reason to choose a specific subtype of the upper bound.

+14
Jul 09 '15 at 20:18
source share

With sneakyThrow type T is a limited typical type variable without a specific type (since there is no type from which the type can arise).

With nonSneakyThrow type T is the same type as the argument, so in your example T of nonSneakyThrow(e); - Exception . Since testSneaky() does not declare a thrown Exception , an error is displayed.

Please note that this is a known Generics interference with verified exceptions.

0
Jul 09 '15 at 11:54 on
source share



All Articles