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

Java 8中异常类型推断的一个独特特性,java,generics,java-8,type-inference,Java,Generics,Java 8,Type Inference,在这个网站上为另一个答案编写代码时,我遇到了这样一个特点: static void testSneaky() { final Exception e = new Exception(); sneakyThrow(e); //no problems here nonSneakyThrow(e); //ERRROR: Unhandled exception: java.lang.Exception } @SuppressWarnings("unchecked") static &

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

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;
}
static void testskilly(){
最终异常e=新异常();
鬼鬼祟祟地走(e);//这里没问题
nonSneakyThrow(e);//错误:未处理的异常:java.lang.exception
}
@抑制警告(“未选中”)
静态空穴潜行(可丢弃的t)抛出t{
投掷(T)T;
}
静态无效非整洁抛出(T T)抛出T{
掷t;
}
首先,我很困惑为什么
skillythrow
调用对编译器来说是可以的。当没有提到任何未检查的异常类型时,它为
T
推断出了什么可能的类型


第二,承认这是可行的,那么为什么编译器会对
nonSneakyThrow
调用抱怨呢?它们看起来非常相似。

对于
skillythrow
,类型
T
是一个没有特定类型的有界泛型类型变量(因为没有类型的来源)

对于
nonSneakyThrow
,类型
T
与参数的类型相同,因此在您的示例中,
nonSneakyThrow(e)的
T
异常
。由于
testskilly()
没有声明抛出的
异常
,因此会显示一个错误


请注意,这是已知的泛型对已检查异常的干扰。

skillythrow
的T推断为
RuntimeException
。这可以从类型推断的langauge规范()中得到遵循

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

表单
抛出α
的边界纯粹是信息性的:它指导解析以优化α的实例化,以便在可能的情况下,它不是已检查的异常类型

这不会影响任何事情,但它向我们指出了解析部分(18.4),该部分获得了关于推断异常类型的更多信息,其中有一个特例:

。。。否则,如果绑定集包含
抛出αi
,并且αi的正确上界最多为
异常
可抛出
、和
对象
,则Ti=
运行时异常

这种情况适用于
sleekythrow
——唯一的上限是
Throwable
,因此
T
根据规范推断为
RuntimeException
,因此它编译。方法的主体是无关紧要的——未检查的强制转换在运行时成功,因为它实际上没有发生,留下的方法可能会破坏编译时检查的异常系统


nonSneakyThrow
不会编译,因为该方法的
T
具有
Exception
的下限(即
T
必须是
Exception
的超类型,或
Exception
本身),这是一个选中的异常,因为调用它的类型不同,因此,
T
被推断为
异常

如果类型推断为类型变量生成一个单一的上界,则通常选择上界作为解决方案。例如,如果
TSo For
skillythrow
实际上没有推断出任何特定的类型,那么“cast”就是这样一个未定义的类型?我想知道这到底发生了什么。@Maksym你一定是指
skillythrow
调用。Java 7规范中没有关于
抛出T
形式推断的特殊规定。小细节:在
非整洁抛出
中,
T
必须是
异常
,而不是“异常的超类型”
异常
,因为它正是在调用站点编译时声明的参数类型。@llogiq如果我正确地阅读了规范,它的下界是
Exception
,上界是
Throwable
,因此最小上界是得到的推断类型,is
Exception
@llogiq请注意,参数的类型只设置了较低的类型界限,因为参数的任何超类型也是可以接受的。“或
Exception
本身”这句话可能对读者有所帮助,但一般来说,应该注意,规范总是使用术语“子类型”和“超类型”在“包括自身”的意义上,…在上一个示例片段中,
X:>RuntimeException
的含义是什么?
interface Action<T extends Throwable>
{
    void doIt() throws T;
}

<T extends Throwable> void invoke(Action<T> action) throws T
{
    action.doIt(); // throws T
}    
    invoke( ()->{} ); 
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)