【问题标题】:Why cannot run() of Runnable throw checked Exceptions?为什么Runnable的run()不能抛出checked Exceptions?
【发布时间】:2012-07-10 09:08:15
【问题描述】:

根据JCIP 的第 6.3.2 节:

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

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

【问题讨论】:

标签: java java.util.concurrent


【解决方案1】:

它不能抛出已检查的异常,因为从第一个版本开始它没有被声明为抛出已检查的异常,更改它太危险了。

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

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

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

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

Callable<Void> callable = new Callable<Void>() {
    public Void call() {
        // do something
        return null;
    }
};

【讨论】:

  • “太危险” == 会破坏数十万现有程序的兼容性。这是一个很大的“不”!
  • @peter Callable 只说调用的返回是 void 类型?对于可以从 call() 抛出的异常,它是否说明了什么?
  • 没有throws 子句的事实表明没有检查异常。即使父母声明throws Exception 并不意味着孩子必须这样做。但是,如果父级没有“抛出”已检查异常,则子级无法抛出已检查异常。
  • @PeterLawrey 我明白了为什么 runnable 不能抛出检查异常。但是我在之前的评论中的问题是 Callable 向用户表明了什么?它不是简单地说这个可调用对象不会从它的 call() 方法返回任何值吗?是不是也说 call() 方法不能抛出异常?
  • @Subhra 这部分代码没有说明是否可以抛出已检查的异常。您将不得不阅读更多代码。
【解决方案2】:

这不是问题的答案。相反,它是Lukas Eder's answer 的后续,展示了另一种将检查异常偷运到静态不允许的地方的方法。这依赖于这样一个事实:如果使用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 巫术。永远不要这样做。除了在派对上给人留下深刻印象。

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

    • 特别令人困惑的是,invokeAndWait 的当前文档讨论了“如果 Runnable.run 方法抛出未捕获的异常”会发生什么。我敢肯定,很多人都读过这篇文章并愉快地尝试为他们的重写 run()s 声明一个例外条款。
    【解决方案4】:

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

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

    【讨论】:

      【解决方案5】:

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

      【讨论】:

      • 没错。正如其他人所说,很明显,字面意思是“run() 没有声明抛出任何东西”,但我认为真正的问题是为什么会这样。答案是,run() 是由一个线程调用的,它在 run() 返回后终止或返回到池中,那么谁来捕捉它呢?因此,永远不会有抛出异常有用的情况。
      【解决方案6】:

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

      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

      话虽如此,但不要这样做! ;-)

      【讨论】:

      • 你甚至不需要使用Unsafe。从无参数构造函数抛出的已检查异常会转义 newInstance 调用。我会添加一个答案...
      • 天啊,链接到的文章中已经提到了。现在我看起来像个笨蛋。
      • @TomAnderson:好吧,无论如何都值得一提。
      猜你喜欢
      • 1970-01-01
      • 2011-02-05
      • 2012-01-24
      • 1970-01-01
      • 2011-01-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多