Java 8中异常类型推断的一个独特特征

Mar*_*nik 83 java generics type-inference java-8

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

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;
}
Run Code Online (Sandbox Code Playgroud)

首先,我很困惑为什么sneakyThrow对编译器调用是好的.T在没有提到未经检查的异常类型的任何地方时,它推断出什么可能的类型?

其次,接受这是有效的,为什么编译器会在nonSneakyThrow电话中抱怨?它们看起来非常相像.

the*_*oop 64

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

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

表单的边界throws ?纯粹是信息性的:它指示解析以优化α的实例化,以便在可能的情况下,它不是经过检查的异常类型.

这不会影响任何事情,但它指向了解决方案部分(18.4),它提供了有关特殊情况的推断异常类型的更多信息:

......否则,如果绑定集包含throws ?i,和α我的正常上限的,顶多Exception,ThrowableObject,然后TI = RuntimeException.

这种情况适用于sneakyThrow- 唯一的上限是Throwable,所以T推断是RuntimeException按照规范,所以它编译.该方法的主体是无关紧要的 - 未经检查的转换在运行时成功,因为它实际上不会发生,留下一个方法可以打败编译时检查的异常系统.

nonSneakyThrow不编译,因为该方法的T下限Exception(即T必须是超类型Exception或其Exception本身),这是一个经过检查的异常,因为它被调用的类型,因此T被推断为Exception.

  • "或"例外本身"这个短语可能对读者有所帮助,但一般来说,应该注意的是,规范总是在"包括自身"的意义上使用术语"子类型"和"超类型"...... (2认同)

Zho*_*gYu 17

如果类型推断为类型变量生成单个上限,则通常选择上限作为解.例如,如果T<<Number,解决方案是T=Number.虽然Integer,Float等,也能满足约束条件,没有充分的理由选择了他们Number.

throws T在java 5-7中也是如此:T<<Throwable => T=Throwable.(偷偷摸摸的抛出解决方案都有明确的<RuntimeException>类型参数,否则<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
}    
Run Code Online (Sandbox Code Playgroud)

如果我们用空的lambda调用,那么T推断为什么?

    invoke( ()->{} ); 
Run Code Online (Sandbox Code Playgroud)

唯一的约束T是上限Throwable.在java8的早期阶段,T=Throwable将推断出来.见我提交的这份报告.

但这是非常愚蠢的Throwable,从空块中推断出一个经过检查的异常.报告中提出了一个解决方案(显然是由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)
Run Code Online (Sandbox Code Playgroud)

即如果上限是ExceptionThrowable,则选择RuntimeException作为解决方案.在这种情况下,一个很好的理由来选择上限的特定亚型.