Java 8中exceptiontypes推断的一个特殊function

当在这个网站上编写另一个答案的代码时,我遇到了这个特点:

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

首先,我很困惑为什么sneakyThrow调用对编译器是可以的。 当没有提及任何未经检查的exceptiontypes时,它推断T是什么types的?

其次,接受这个工作,为什么编译器会抱怨nonSneakyThrow调用呢? 他们看起来非常相似。

sneakyThrow的T被推断为RuntimeException 。 这可以从types推断的语言规范( http://docs.oracle.com/javase/specs/jls/se8/html/jls-18.html

首先,第18.1.3节有一个注释:

forms的边界throws α纯粹是信息性的:它指导parsing来优化α的实例化,如果可能的话,它不是一个检查的exceptiontypes。

这并不影响任何事情,但它指向了解决scheme部分(18.4),该部分已经获得了有关特殊情况的推断exceptiontypes的更多信息:

…否则,如果绑定集合包含throws αi ,并且throws αi的适当上限至多是ExceptionThrowableObject ,则Ti = RuntimeException

这种情况适用于sneakyThrow – 唯一的上限是Throwable ,所以根据规范推断TRuntimeException ,所以它编译。 该方法的主体是不重要的 – 未经检查的转换在运行时成功,因为它实际上并没有发生,留下一个方法,可以击败编译时检查exception系统。

nonSneakyThrow不能编译,因为该方法的T有一个下限的Exception (即T必须是一个超types的Exception ,或Exception本身),这是一个检查exception,由于它被调用的types,所以T得到推断作为Exception

如果types推导产生一个typesvariables的单一上界,通常select上界作为解决scheme。 例如,如果T<<Number ,则解决scheme是T=Number 。 虽然IntegerFloat等也可以满足约束条件,但没有理由select它们。

在java 5-7中, T<<Throwable => T=Throwable也是如此: T<<Throwable => T=Throwable 。 (Sneaky throw解决scheme都有明确的<RuntimeException>types参数,否则推断为<Throwable> 。)

在java8中,随着lambda的引入,这成为问题。 考虑这种情况

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

如果我们调用一个空的lambda, T会被推断为什么?

  invoke( ()->{} ); 

T上唯一的约束是Throwable的上限。 在java8的早期阶段, T=Throwable将被推断。 看到我提交的这份报告 。

但是,这非常愚蠢,从一个空白块中推断Throwable ,一个检查的exception。 报告中提出了一个解决scheme(JLS明显采用这种解决scheme)

 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) 

即如果上限是ExceptionThrowable ,则selectRuntimeException作为解决scheme。 在这种情况下,有一个很好的理由来select一个特定的上界子types。

sneakyThrow ,typesT是一个没有特定types的有界通用typesvariables(因为没有types可能来自哪里)。

使用nonSneakyThrow ,typesT与参数types相同,因此在你的例子中, nonSneakyThrow(e);T nonSneakyThrow(e);Exception 。 由于testSneaky()不声明抛出的Exception ,所以会显示一个错误。

请注意,这是generics与已检查的exception的已知干扰。