【问题标题】:Is there a way to make Runnable's run() throw an exception?有没有办法让 Runnable 的 run() 抛出异常?
【发布时间】:2019-09-17 14:14:56
【问题描述】:

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

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

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

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

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

如何解决这个限制?

【问题讨论】:

标签: java android runnable throws


【解决方案1】:

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

isDone() 返回真时,您调用FutureTask.get()。现在,如果你的Callable 抛出了一个Exception,那么FutureTask.get() 也将抛出一个Exception,你将能够使用Exception.getCause() 访问原始异常。

【讨论】:

    【解决方案2】:

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

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

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

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

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

    【讨论】:

    • 这样一个简单的解决方案只是不考虑“在盒子里”。当然,Runnable 只是一个简单的界面,我们可以自己制作。对于线程用例没有用,但对于传递不同的“可运行”代码块来说是完美的。
    • 这意味着我们必须创建另一个 MyTimerTask 或 MyThread 才能使用 MyRunable ....
    【解决方案3】:

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

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

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

    【讨论】:

    • 第一部分不是一个好的论据。 “如果main() 抛出了一个检查异常,什么会捕获它?” “如果run() 抛出未经检查的异常,什么会捕获它?”
    【解决方案4】:

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

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

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

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

    如果不是Thread 目标,请不要使用Runnable。例如,Callable 可能更合适。

    【讨论】:

    • 但这是否会导致进程在抛出时崩溃??
    • @DineshVG 不,只有 JVM 中的错误会导致真正的崩溃。默认异常处理程序只打印异常。如果你习惯于看到进程在那之后退出,那是因为该线程是唯一运行的线程,它终止了。
    • 我尝试(在 android 仪器测试用例中)使用它进行肥皂调用,如果我从肥皂调用中得到 400,我会抛出异常。在启动测试用例时,从线程调用此肥皂调用。该线程使用此t.getUncaughtExceptionHandler().uncaughtException(t, ex); 将其扔给仪器测试用例。添加这一行会导致进程崩溃!。不知道为什么。
    • @DineshVG 在那种环境中,Thread.getDefaultUncaughtExceptionHandler() 是否返回 null?如果不是,结果的类型是什么?如果不是调用uncaughtException(),而是将检查的异常包装在RuntimeException 中并抛出它,会发生什么?
    • @DineshVG Android 可能正在设置一个默认的未捕获异常处理程序来执行此操作。这就是为什么我问Thread.getDefaultUncaughtExceptionHandler() 是否返回null;如果没有,Android 会提供默认值,以便提供报告等。但您可以将其设置为做您想做的事情。还有更多信息here.
    【解决方案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;
    
    }
    

    【讨论】:

      【解决方案6】:

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

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

      那么有哪些情况和选择呢? 在下面的文本中,Runnable 代表任何未声明异常或声明的异常对于手头的用例而言过于有限的功能接口。

      1. 您已经在自己的某个地方声明了Runnable,并且可以将Runnable 替换为其他内容。
        1. 考虑将Runnable 替换为Callable&lt;Void&gt;。基本相同,但允许抛出异常;最后必须return null,这有点烦人。
        2. 考虑将 Runnable 替换为您自己的自定义 @FunctionalInterface,它可以准确地抛出您想要的那些异常。
      2. 您已经使用了 API,并且可以使用替代方法。例如,某些 Java API 已重载,因此您可以使用 Callable&lt;Void&gt; 而不是 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 版本中被删除,因为泛型类型信息不仅在编译时处理,而且在运行时处理。

      【讨论】:

        【解决方案7】:

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

        【讨论】:

          【解决方案8】:

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

          【讨论】:

            【解决方案9】:

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

            【讨论】:

            • 亲爱的先生,当它发生时,您将如何处理这个 RuntimeException?
            • 仅此并不能回答问题。
            猜你喜欢
            • 2010-12-08
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-07-11
            • 1970-01-01
            • 2017-02-14
            • 2014-06-06
            • 1970-01-01
            相关资源
            最近更新 更多