Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
809 views
in Technique[技术] by (71.8m points)

generics - A peculiar feature of exception type inference in Java 8

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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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, and Object, 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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...