有没有办法让Runnable的run()抛出异常?

Reg*_*kie 67 java android runnable throws

我在实现Runnable的类中调用run()的方法被设计为抛出异常.

但Java编译器不会让我这样做,并建议我用try/catch包围它.

问题是,通过try/catch包围它,我使特定的 run()无用.我确实想抛出那个例外.

如果我throwsrun()本身指定,编译器会抱怨Exception is not compatible with throws clause in Runnable.run().

通常我完全没有让 run()抛出异常.但我有独特的情况,我必须具备该功能.

如何解决这个限制?

Ale*_*tin 72

您可以使用a Callable,将其提交给a ExecutorService并等待结果 FutureTask.isDone()返回ExecutorService.submit().

isDone()返回true时,你打电话FutureTask.get().现在,如果你Callable已经抛出了一个wiill ,Exception那么你将可以使用原始的Exception .FutureTask.get()ExceptionException.getCause()


Ern*_*ill 20

如果run()抛出一个已检查的异常,会有什么结果呢?run()由于您没有编写调用它的代码,因此无法将该调用封装在处理程序中.

您可以在run()方法中捕获已检查的异常,并RuntimeException在其位置抛出未检查的异常(即).这将使用堆栈跟踪终止线程; 也许这就是你所追求的.

如果你希望你的run()方法在某个地方报告错误,那么你可以只为run()方法的catch块提供一个回调方法来调用; 该方法可以在某处存储异常对象,然后您感兴趣的线程可以在该位置找到该对象.

  • 第一部分不是一个好的论点。“如果`main()`抛出了一个已检查的异常,将会捕获到什么?” “如果`run()`抛出未经检查的异常,将会捕获到什么?” (2认同)

Ale*_*yak 18

如果你想传递一个实现类RunnableThread框架,那么你必须通过该框架的规则玩,看欧内斯特·弗里德曼-希尔的回答,为什么这样做,否则是一个坏主意.

不过,我有一种预感,你想run直接在你的代码中调用方法,所以你的调用代码可以处理异常.

这个问题的答案很简单.不要使用Runnable来自线程库的接口,而是使用修改后的签名创建自己的接口,该签名允许抛出已检查的异常,例如

public interface MyRunnable
{
    void myRun ( ) throws MyException;
}
Run Code Online (Sandbox Code Playgroud)

您甚至可以创建一个适配器,将该接口转换为真实的Runnable(通过处理已检查的异常),适用于Thread框架.


eri*_*son 16

是的,有一种方法可以从方法中抛出一个已检查的异常run(),但它太可怕了,我不会分享它.

这是你可以做的事情; 它使用与运行时异常相同的机制:

@Override
public void run() {
  try {
    /* Do your thing. */
    ...
  } catch (Exception ex) {
    Thread t = Thread.currentThread();
    t.getUncaughtExceptionHandler().uncaughtException(t, ex);
  }
}
Run Code Online (Sandbox Code Playgroud)

正如其他人所指出的那样,如果你的run()方法确实是a的目标Thread,那么抛出异常是没有意义的,因为它是不可观察的; 抛出异常与不抛出异常(无)具有相同的效果.

如果它不是Thread目标,请不要使用Runnable.例如,也许Callable更合适.


Chr*_*jer 6

有些人试图说服您必须遵守规则。听着,但听不听,你要根据自己的情况自行决定。现实是“你应该遵守规则”(而不是“你必须遵守规则”)。请注意,如果您不遵守规则,可能会产生后果。

这种情况不仅适用于RunnableJava 8 的情况,而且在 Streams 和其他引入函数式接口而无法处理检查异常的地方也经常出现。例如,ConsumerSupplierFunction等都BiFunction被声明为没有处理检查异常的设施。

那么有哪些情况和选择呢?在下面的文本中,Runnable代表任何不声明异常的功能接口,或者声明的异常对于当前的用例来说过于有限。

  1. 您自己在某个地方声明了Runnable,并且可以Runnable用其他东西替换。
    1. 考虑替换RunnableCallable<Void>. 基本上是一样的,但是允许抛出异常;return null最后不得不这样做,这是一个轻微的烦恼。
    2. 考虑Runnable用您自己的自定义替换@FunctionalInterface,该自定义可以准确地抛出您想要的异常。
  2. 您已经使用了 API,并且可以使用替代方案。例如,某些 Java API 已重载,因此您可以Callable<Void>使用Runnable.
  3. 您已经使用了 API,并且没有其他选择。在这种情况下,您仍然没有没有选择。
    1. 您可以将异常包装在RuntimeException.
    2. 您可以使用未经检查的强制转换将异常破解为 RuntimeException。

您可以尝试以下操作。这有点像黑客,但有时我们需要黑客。因为,是否应该检查或不检查异常是由其类型定义的,但实际上应该根据情况来定义。

@FunctionalInterface
public interface ThrowingRunnable extends Runnable {
    @Override
    default void run() {
        try {
            tryRun();
        } catch (final Throwable t) {
            throwUnchecked(t);
        }
    }

    private static <E extends RuntimeException> void throwUnchecked(Throwable t) {
        throw (E) t;
    }

    void tryRun() throws Throwable;
}
Run Code Online (Sandbox Code Playgroud)

我更喜欢这个,new RuntimeException(t)因为它的堆栈跟踪更短。

您现在可以执行以下操作:

executorService.submit((ThrowingRunnable) () -> {throw new Exception()});
Run Code Online (Sandbox Code Playgroud)

免责声明:当泛型类型信息不仅在编译时处理,而且在运行时处理时,以这种方式执行未经检查的强制转换的能力实际上可能会在 Java 的未来版本中被删除。


Sin*_*ani 5

@FunctionalInterface
public interface CheckedRunnable<E extends Exception> extends Runnable {

    @Override
    default void run() throws RuntimeException {
        try {
            runThrows();
        }
        catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    void runThrows() throws E;

}
Run Code Online (Sandbox Code Playgroud)