86

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

但是 Java 编译器不允许我这样做,并建议我用 try/catch 包围它。

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

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

通常我完全可以不让 run()抛出异常。但我有一个独特的情况,我必须拥有该功能。

如何解决此限制?

4

9 回答 9

90

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

isDone()返回 true 你调用FutureTask.get(). 现在,如果你Callable已经抛出了一个,Exception那么也FutureTask.get()将抛出一个Exception原始异常,你将能够使用Exception.getCause().

于 2012-07-20T17:32:37.103 回答
36

如果您想将实现的类传递RunnableThread框架中,那么您必须遵守该框架的规则,请参阅 Ernest Friedman-Hill 的回答,为什么这样做是一个坏主意。

不过,我有一种预感,您想run直接在代码中调用方法,这样您的调用代码就可以处理异常。

这个问题的答案很简单。不要使用RunnableThread 库中的接口,而是使用修改后的签名创建自己的接口,允许抛出检查的异常,例如

public interface MyRunnable
{
    void myRun ( ) throws MyException;
}

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

于 2012-07-20T17:36:42.073 回答
27

如果run()抛出一个检查异常,什么会捕获它?您无法将该run()调用包含在处理程序中,因为您不编写调用它的代码。

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

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

于 2012-07-20T17:28:41.497 回答
19

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

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

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

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

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

于 2012-07-20T17:34:44.633 回答
6
@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;

}
于 2018-11-20T17:07:13.940 回答
2

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

这种情况不仅适用于 .java 的情况Runnable,而且在 Java 8 的情况下也非常频繁地出现在 Streams 和其他引入了功能接口而无法处理已检查异常的地方的上下文中。例如,、 、Consumer等等Supplier都被声明为没有处理检查异常的设施。FunctionBiFunction

那么有哪些情况和选择呢?在下面的文本中,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。

您可以尝试以下方法。这有点像 hack,但有时我们需要 hack。因为,一个异常应该被检查还是不被检查是由它的类型定义的,但实际上应该由情况来定义。

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

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

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

executorService.submit((ThrowingRunnable) () -> {throw new Exception()});

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

于 2018-11-24T08:52:45.117 回答
0

你的要求没有任何意义。如果你想通知被调用线程发生了异常,你可以通过回调机制来做到这一点。这可以通过处理程序或广播或您能想到的任何其他方式。

于 2012-07-20T17:29:22.643 回答
0

我认为侦听器模式可能会帮助您解决这种情况。如果您的方法中发生异常,请run()使用 try-catch 块并在 catch 中发送异常事件通知。然后处理您的通知事件。我认为这将是一种更清洁的方法。此 SO 链接为您提供了指向该方向的有用指针。

于 2012-07-20T17:32:22.283 回答
-1

最简单的方法是定义自己的异常对象,它扩展RuntimeException类而不是Exception类。

于 2016-03-15T03:02:25.970 回答