19

根据JCIP第 6.3.2 节:

Runnable 是一个相当有限的抽象;run 不能返回值或抛出检查异常。

run()不能返回一个值,因为它的返回类型是 void 但为什么不能抛出一个检查异常?

4

6 回答 6

29

它不能抛出受检异常,因为它没有被声明为从第一个版本中抛出受检异常,并且更改它太危险了。

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

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

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

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

Callable<Void> callable = new Callable<Void>() {
    public Void call() {
        // do something
        return null;
    }
};
于 2012-07-10T09:14:40.577 回答
5

这不是问题的答案。相反,它是Lukas Eder 的回答的后续,展示了另一种将检查异常偷运到静态不允许的地方的方法。这依赖于这样一个事实,即如果使用 调用无参数构造函数newInstance,则它抛出的任何已检查异常都会向上转义。

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

}

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

于 2012-07-10T16:56:31.407 回答
4

run()不能抛出检查异常,因为它没有声明这样做。你不能在不声明的情况下抛出已检查的异常。

您也不能在覆盖或实现另一个不引发该异常的方法的方法上声明检查异常。因此, 的实现Runnable不能简单地将throws子句添加到run().

于 2012-07-10T09:14:55.510 回答
2

您总是可以不安全地抛出已检查的异常:

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

在此处查看全文:

http://java.dzone.com/articles/throwing-undeclared-checked

另一种选择:

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

这显示在这里:

http://java.dzone.com/articles/throw-checked-exceptions

说了这么多,千万别做!;-)

于 2012-07-10T09:21:20.613 回答
2

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

于 2012-08-05T10:00:43.607 回答
2

如果您查看Runnable Interface,您会发现该void run()方法未声明为抛出任何已检查的异常,并且您的 Thread 类实现了 Runnable Interface 。

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

于 2012-07-10T09:25:50.227 回答