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
的适当上限至多是Exception
,Throwable
和Object
,则Ti =RuntimeException
。
这种情况适用于sneakyThrow
– 唯一的上限是Throwable
,所以根据规范推断T
是RuntimeException
,所以它编译。 该方法的主体是不重要的 – 未经检查的转换在运行时成功,因为它实际上并没有发生,留下一个方法,可以击败编译时检查exception系统。
nonSneakyThrow
不能编译,因为该方法的T
有一个下限的Exception
(即T
必须是一个超types的Exception
,或Exception
本身),这是一个检查exception,由于它被调用的types,所以T
得到推断作为Exception
。
如果types推导产生一个typesvariables的单一上界,通常select上界作为解决scheme。 例如,如果T<<Number
,则解决scheme是T=Number
。 虽然Integer
, Float
等也可以满足约束条件,但没有理由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)
即如果上限是Exception
或Throwable
,则selectRuntimeException
作为解决scheme。 在这种情况下,有一个很好的理由来select一个特定的上界子types。
用sneakyThrow
,typesT
是一个没有特定types的有界通用typesvariables(因为没有types可能来自哪里)。
使用nonSneakyThrow
,typesT
与参数types相同,因此在你的例子中, nonSneakyThrow(e);
的T
nonSneakyThrow(e);
是Exception
。 由于testSneaky()
不声明抛出的Exception
,所以会显示一个错误。
请注意,这是generics与已检查的exception的已知干扰。