Java 为什么Runnable的run()不能抛出已检查的异常?

Java 为什么Runnable的run()不能抛出已检查的异常?,java,java.util.concurrent,Java,Java.util.concurrent,根据第6.3.2节: Runnable是一个相当有限的抽象;run无法返回值或引发选中的异常 run()无法返回值,因为其返回类型为void,但为什么它不能抛出选中的异常 它无法引发选中的异常,因为它没有从第一个版本声明为引发选中的异常,并且更改它太危险了 最初,Runnable仅用于包装的线程,并且假定开发人员希望捕获所有已检查的异常并处理它们,而不是将它们记录到System.err Callable是在您可以将单个任务添加到Executor时添加的,您可以在Future中捕获结果以及引发的任

根据第6.3.2节:

Runnable是一个相当有限的抽象;run无法返回值或引发选中的异常


run()
无法返回值,因为其返回类型为void,但为什么它不能抛出选中的异常

它无法引发选中的异常,因为它没有从第一个版本声明为引发选中的异常,并且更改它太危险了

最初,
Runnable
仅用于包装的
线程
,并且假定开发人员希望捕获所有已检查的异常并处理它们,而不是将它们记录到
System.err

Callable
是在您可以将单个任务添加到
Executor
时添加的,您可以在
Future
中捕获结果以及引发的任何异常

Callable
现在允许您返回一个值并可选地声明一个选中的异常

顺便说一句:你可以说你不想从可调用对象返回或抛出已检查异常的一种方法是使用

Callable<Void> callable = new Callable<Void>() {
    public Void call() {
        // do something
        return null;
    }
};
Callable Callable=new Callable(){
公开作废通知(){
//做点什么
返回null;
}
};
无法抛出选中的异常,因为它未声明要这样做。您不能在不声明的情况下抛出已检查的异常


您也不能在重写或实现另一个不引发该异常的方法的方法上声明选中的异常。因此,
Runnable
的实现不能简单地将
throws
子句添加到它们的
run()

实现中。您可以始终不安全地抛出选中的异常:

import java.lang.reflect.Field;
import sun.misc.Unsafe;

public class UnsafeSample {
    public void methodWithNoDeclaredExceptions( ) {
        Unsafe unsafe = getUnsafe();
        unsafe.throwException( new Exception( "this should be checked" ) );
    }

    private Unsafe getUnsafe() {
        try {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            return (Unsafe) field.get(null);
        } catch(Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static void main( String[] args ) {
        new UnsafeSample().methodWithNoDeclaredExceptions();
    }
}
请参阅此处的全文:

另一种选择:

public class Test {
    public static void main(String[] args) {
        doThrow(new SQLException());
    }

    public static void doThrow(Exception e) {
        Test.<RuntimeException> doThrow0(e);
    }

    @SuppressWarnings("unchecked")
    public static <E extends Exception> void doThrow0(Exception e) throws E {
        throw (E) e;
    }
}
公共类测试{
公共静态void main(字符串[]args){
doThrow(新的SQLException());
}
公共静态void doThrow(例外e){
测试doThrow0(e);
}
@抑制警告(“未选中”)
公共静态void doThrow0(异常e)抛出e{
投掷(E)E;
}
}
这显示在这里:

既然这样说了,就别做了!;-)

如果查看,您会发现
void run()
方法没有声明为引发任何选中的异常,并且您的线程类实现了Runnable接口


表示如果在接口/超类中未声明方法m1,则该方法m1无法引发异常

这不是问题的答案。更确切地说,它是对的后续,展示了另一种将已检查异常走私到静态不允许的地方的方法。这取决于这样一个事实:如果调用无参数构造函数,它抛出的任何选中异常都会向上转义

public class Thrower {

    private static final ThreadLocal<Exception> toThrow = new ThreadLocal<Exception>();

    public static void throwUnsafely(Exception e) {
        try {
            toThrow.set(e);
            Thrower.class.newInstance();
        } catch (InstantiationException f) {
            throw new RuntimeException("unexpected exception while throwing expected exception", f);
        } catch (IllegalAccessException f) {
            throw new RuntimeException("unexpected exception while throwing expected exception", f);
        } finally {
            toThrow.remove();
        }
    }

    private Thrower() throws Exception {
        throw toThrow.get();
    }

}
公共级投掷者{
private static final ThreadLocal toThrow=new ThreadLocal();
公共静态无效throwUnsafely(例外e){
试一试{
toThrow.set(e);
strower.class.newInstance();
}捕获(实例化异常f){
抛出新的RuntimeException(“抛出预期异常时出现意外异常”,f);
}捕获(非法访问例外f){
抛出新的RuntimeException(“抛出预期异常时出现意外异常”,f);
}最后{
toThrow.remove();
}
}
private Thrower()抛出异常{
扔到下面去;
}
}

这是A级真正古老的黑帽爪哇巫术。永远不要这样做。除了在聚会上给人留下深刻印象之外。

我认为在Runnable中保留签名void run()的动机是,它不是像其他方法那样被调用,而是被设计为由CPU线程调度程序调用。如果是这样,谁将接收其返回值,谁将处理由此引发的检查异常。出现在Java5.0中,用于处理线程引发的未捕获异常。Executor框架将返回值或抛出的异常(ExecutionException中的包装器)保存为跨线程(如外部类实例)共享的某些对象的状态,并将这些状态提供给未来的调用程序(在其他线程中运行)。().

如果需要返回值,最好使用ExecutorServiceErr中的Callable接口,因为它被声明为不可调用?可能重复的“太危险”==会破坏数十万现有程序的兼容性。这是一个很大的“不不不”@peter Callable只说通话返回的类型为void?对于可以从call()抛出的异常,它到底说了什么吗?没有
throws
子句的事实表明没有检查异常。即使父级声明
抛出异常
,也不意味着子级必须这样做。但是,如果父级未“抛出”选中的异常,则子级无法抛出选中的异常。@PeterLawrey我明白了为什么runnable不能抛出选中的异常。但在我之前的谈话中,我的问题是Callable向用户表明了什么?它不是简单地说这个可调用函数不会从它的call()方法返回任何值吗?它是否也说call()方法不能抛出任何异常?@Subhra这部分代码没有说明是否可以抛出选中的异常。您需要阅读更多的代码,甚至不需要使用
safe
。从无参数构造函数抛出的已检查异常转义
newInstance
调用。我将添加一个答案…哦,伙计,这在链接到的文章中有所涉及。现在我看起来像个傻瓜。@TomAnderson:无论如何,这是值得一提的。特别令人困惑的是,当前的文档讨论了“如果Runnable.run方法抛出未捕获的异常”会发生什么。我相信很多人都读过这篇文章,并愉快地试图为他们的覆盖run()声明一个exceptions子句。正如其他人所说,很明显