可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
A method I am calling in run() in a class that implements Runnable) is designed to be throwing an exception.
But the Java compiler won't let me do that and suggests that I surround it with try/catch.
The problem is that by surrounding it with a try/catch I make that particular run() useless. 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()
.
Ordinarily I'm totally fine with not letting run() throw an exception. But I have unique situation in which I must have that functionality.
How to I work around this limitation?
回答1:
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.
I have a hunch, though, that you want to call run
method directly in your code, so your calling code can process the exception.
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, e.g.
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.
回答2:
You can use a Callable
instead, submitting it to an ExecutorService
and waiting for result with FutureTask.isDone()
returned by the ExecutorService.submit()
.
When isDone()
returns true you call 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()
.
回答3:
If run()
threw a checked exception, what would catch it? There's no way for you to enclose that run()
call in a handler, since you don't write the code that invokes it.
You can catch your checked exception in the run()
method, and throw an unchekced exception (i.e., RuntimeException
) in its place. 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; that method could store the exception object somewhere, and then your interested thread could find the object in that location.
回答4:
Yes, there is a way to throw a checked exception from the run()
method, but it's so terrible I won't share it.
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; throwing an exception has the same effect as not throwing an exception (none).
If it's not a Thread
target, don't use Runnable
. For example, perhaps Callable
is a better fit.
回答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:
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. 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.
回答7:
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. For example, Consumer
, Supplier
, Function
, BiFunction
and so on have all been declared without facilities to deal with checked exceptions.
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.
- You've declared
Runnable
somewhere yourself, and could replace Runnable
with something else.
- Consider replacing
Runnable
with Callable<Void>
. Basically the same thing, but allowed to throw exceptions; and has to return null
in the end, which is a mild annoyance.
- Consider replacing
Runnable
with your own custom @FunctionalInterface
that can throw exactly those exceptions that you want.
- You've used an API, and alternatives are available. For example, some Java APIs are overloaded so you could use
Callable<Void>
instead of Runnable
.
- You've used an API, and there are no alternatives. In that case, you're still not out of options.
- You can wrap the exception in
RuntimeException
.
- You can hack the exception into a RuntimeException by using an unchecked cast.
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.
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.
回答8:
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.
回答9:
The easiest way is to define your own exception object which extend the RuntimeException
class instead of the Exception
class.