简体   繁体   English

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

[英]Is there a way to make Runnable's run() throw an exception?

A method I am calling in run() in a class that implements Runnable ) is designed to be throwing an exception.我在实现Runnable的 class 中调用run()的方法)旨在引发异常。

But the Java compiler won't let me do that and suggests that I surround it with try/catch.但是 Java 编译器不允许我这样做,并建议我用 try/catch 包围它。

The problem is that by surrounding it with a try/catch I make that particular run() useless.问题是,通过用 try/catch 包围它,我使那个特定的 run()变得无用。 I do want to throw that exception.确实想抛出那个异常。

If I specify throws for run() itself, the compiler complains that Exception is not compatible with throws clause in Runnable.run() .如果我为run()本身指定throws ,编译器会抱怨Exception is not compatible with throws clause in Runnable.run()

Ordinarily I'm totally fine with not letting run() throw an exception.通常我完全可以不让run()抛出异常。 But I have unique situation in which I must have that functionality.但是我有一个独特的情况,我必须拥有该功能。

How to I work around this limitation?我该如何解决这个限制?

You can use a Callable instead, submitting it to an ExecutorService and waiting for result with FutureTask.isDone() returned by the ExecutorService.submit() . 您可以使用Callable相反,它提交给ExecutorService ,并等待结果FutureTask.isDone()的返回ExecutorService.submit()

When isDone() returns true you call FutureTask.get() . isDone()返回true时,您调用FutureTask.get() Now, if your Callable has thrown an Exception then FutureTask.get() wiill throw an Exception too and the original Exception you will be able to access using Exception.getCause() . 现在,如果你的Callable抛出了一个Exception那么FutureTask.get()也会抛出一个Exception并且你可以使用Exception.getCause()来访问原始的Exception。

If run() threw a checked exception, what would catch it? 如果run()抛出一个已检查的异常,那会有什么结果呢? There's no way for you to enclose that run() call in a handler, since you don't write the code that invokes it. 您无法在处理程序中包含该run()调用,因为您没有编写调用它的代码。

You can catch your checked exception in the run() method, and throw an unchekced exception (ie, RuntimeException ) in its place. 您可以在run()方法中捕获已检查的异常,并在其位置抛出未检查的异常(即RuntimeException )。 This will terminate the thread with a stack trace; 这将使用堆栈跟踪终止线程; perhaps that's what you're after. 也许这就是你所追求的。

If instead you want your run() method to report the error somewhere, then you can just provide a callback method for the run() method's catch block to call; 如果您希望run()方法在某处报告错误,那么您可以为run()方法的catch块提供一个回调方法来调用; that method could store the exception object somewhere, and then your interested thread could find the object in that location. 该方法可以在某处存储异常对象,然后您感兴趣的线程可以在该位置找到该对象。

If you want to pass a class that implements Runnable into the Thread framework, then you have to play by that framework's rules, see Ernest Friedman-Hill's answer why doing it otherwise is a bad idea. 如果你想将一个实现Runnable的类传递给Thread框架,那么你必须遵循该框架的规则,参见Ernest Friedman-Hill的答案,为什么这样做是一个坏主意。

I have a hunch, though, that you want to call run method directly in your code, so your calling code can process the exception. 不过,我有一种预感,你想直接在代码中调用run方法,因此你的调用代码可以处理异常。

The answer to this problem is easy. 这个问题的答案很简单。 Do not use Runnable interface from Thread library, but instead create your own interface with the modified signature that allows checked exception to be thrown, eg 不要使用Thread库中的Runnable接口,而是使用修改后的签名创建自己的接口,该签名允许抛出已检查的异常,例如

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

You may even create an adapter that converts this interface to real Runnable ( by handling checked exception ) suitable for use in Thread framework. 您甚至可以创建一个适配器,将此接口转换为适合在Thread框架中使用的真实Runnable (通过处理已检查的异常)。

Yes, there is a way to throw a checked exception from the run() method, but it's so terrible I won't share it. 是的,有一种方法可以从run()方法中抛出一个已检查的异常,但它太可怕了,我不会分享它。

Here's what you can do instead; 这是你可以做的事情; it uses the same mechanism that a runtime exception would exercise: 它使用与运行时异常相同的机制:

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

As others have noted, if your run() method is really the target of a Thread , there's no point in throwing an exception because it is unobservable; 正如其他人所指出的,如果你的run()方法确实是Thread的目标,那么抛出异常是没有意义的,因为它是不可观察的; throwing an exception has the same effect as not throwing an exception (none). 抛出异常与不抛出异常(无)具有相同的效果。

If it's not a Thread target, don't use Runnable . 如果它不是Thread目标,请不要使用Runnable For example, perhaps Callable is a better fit. 例如,也许Callable更适合。

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

}

Some people try to convince you that you have to play by the rules. 有些人试图说服你必须遵守规则。 Listen, but whether you obey, you should decide yourself depending on your situation. 听着,但不管你是否服从,你应该根据自己的情况决定自己。 The reality is "you SHOULD play by the rules" (not "you MUST play by the rules"). 现实是“你应该遵守规则”(而不是“你必须遵守规则”)。 Just be aware that if you do not play by the rules, there might be consequences. 请注意,如果您不遵守规则,可能会产生后果。

The situation not only applies in the situation of Runnable , but with Java 8 also very frequently in the context of Streams and other places where functional interfaces have been introduced without the possibility to deal with checked exceptions. 这种情况不仅适用于Runnable的情况,而且在Streams和其他已引入功能接口的地方也非常频繁地使用Java 8,而无法处理已检查的异常。 For example, Consumer , Supplier , Function , BiFunction and so on have all been declared without facilities to deal with checked exceptions. 例如, ConsumerSupplierFunctionBiFunction等都已声明,没有设施来处理已检查的异常。

So what are the situations and options? 那么情况和选择是什么? In the below text, Runnable is representative of any functional interface that doesn't declare exceptions, or declares exceptions too limited for the use case at hand. 在下面的文本中, Runnable代表没有声明异常的任何功能接口,或者声明对于手头的用例来说太有限的异常。

  1. You've declared Runnable somewhere yourself, and could replace Runnable with something else. 你自己已经在某个地方声明了Runnable ,并且可以用其他东西替换Runnable
    1. Consider replacing Runnable with Callable<Void> . 考虑用Callable<Void>替换Runnable Basically the same thing, but allowed to throw exceptions; 基本上是一样的,但允许抛出异常; and has to return null in the end, which is a mild annoyance. 并且最后必须return null ,这是一个轻微的烦恼。
    2. Consider replacing Runnable with your own custom @FunctionalInterface that can throw exactly those exceptions that you want. 考虑用您自己的自定义@FunctionalInterface替换Runnable ,它可以准确地抛出您想要的那些异常。
  2. You've used an API, and alternatives are available. 您已经使用了API,并且可以使用其他选项。 For example, some Java APIs are overloaded so you could use Callable<Void> instead of Runnable . 例如,某些Java API被重载,因此您可以使用Callable<Void>而不是Runnable
  3. You've used an API, and there are no alternatives. 您使用过API,但没有其他选择。 In that case, you're still not out of options. 在这种情况下,你仍然没有选择。
    1. You can wrap the exception in RuntimeException . 您可以在RuntimeException包装异常。
    2. You can hack the exception into a RuntimeException by using an unchecked cast. 您可以使用未经检查的强制转换将异常破解为RuntimeException。

You can try the following. 您可以尝试以下方法。 It's a bit of a hack, but sometimes a hack is what we need. 这有点像黑客,但有时黑客是我们需要的。 Because, whether an exception should be checked or unchecked is defined by its type, but practically should actually be defined by the situation. 因为,是否应该检查或取消选中例外是由其类型定义的,但实际上应该根据情况来定义。

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

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

    void tryRun() throws Throwable;
}

I prefer this over new RuntimeException(t) because it has a shorter stack trace. 我比new RuntimeException(t)更喜欢它,因为它有一个较短的堆栈跟踪。

You can now do: 你现在可以这样做:

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

Disclaimer: The ability to perform unchecked casts in this way might actually be removed in future versions of Java, when generics type information is processed not only at compile time, but also at runtime. 免责声明:在Java的未来版本中,当不仅在编译时处理泛型类型信息,而且在运行时处理泛型类型信息时,实际上可以删除以这种方式执行未经检查的强制转换的能力。

I think a listener pattern might help you with this scenario. 我认为听众模式可能会帮助您解决这个问题。 In case of an exception happening in your run() method, use a try-catch block and in the catch send a notification of an exception event. 如果在run()方法中发生异常,请使用try-catch块并在catch中发送异常事件的通知。 And then handle your notification event. 然后处理您的通知事件。 I think this would be a cleaner approach. 我认为这将是一种更清洁的方法。 This SO link gives you a helpful pointer to that direction. 这个SO链接为您提供指向该方向的有用指针。

Yes, you can throw checked exceptions from the run() method.是的,您可以从 run() 方法中抛出已检查的异常。 It can be done with generics by tricking the compiler.可以通过欺骗编译器使用 generics 来完成。 Look at this code:看看这段代码:

public static void main(String[] args) {

    new Main().throwException();

}

public void throwException() {
    Runnable runnable = () -> throwAs(new Exception());

    new Thread(runnable).start();
}

private  <T extends Throwable> void throwAs(Throwable t) throws T {
    throw ( T ) t;
}

This might be helpful if you want to throw checked exceptions from the run() method of Runnable如果您想从 Runnable 的 run() 方法中抛出已检查的异常,这可能会有所帮助

Your requirement doesn't make any sense. 你的要求没有任何意义。 If you want to notify the called of the thread about an exception that happened, you could do that through a call back mechanism. 如果要通知线程调用者发生的异常,可以通过回调机制来实现。 This can be through a Handler or a broadcast or whatever else you can think of. 这可以通过处理程序或广播或您能想到的任何其他内容。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM