可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have some code that might throw both checked and runtime exceptions.
I'd like to catch the checked exception and wrap it with a runtime exception. But if a RuntimeException is thrown, I don't have to wrap it as it's already a runtime exception.
The solution I have has a bit overhead and isn't "neat":
try {
// some code that can throw both checked and runtime exception
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
Any idea for a more elegant way?
回答1:
I use a "blind" rethrow to pass up checked exceptions. I have used this for passing through the Streams API where I can't use lambdas which throw checked exceptions. e.g We have ThrowingXxxxx functional interfaces so the checked exception can be passed through.
This allows me to catch the checked exception in a caller naturally without needing to know a callee had to pass it through an interface which didn't allow checked exceptions.
try {
// some code that can throw both checked and runtime exception
} catch (Exception e) {
throw rethrow(e);
}
In a calling method I can declare the checked exception again.
public void loadFile(String file) throws IOException {
// call method with rethrow
}
/**
* Cast a CheckedException as an unchecked one.
*
* @param throwable to cast
* @param <T> the type of the Throwable
* @return this method will never return a Throwable instance, it will just throw it.
* @throws T the throwable as an unchecked throwable
*/
@SuppressWarnings("unchecked")
public static <T extends Throwable> RuntimeException rethrow(Throwable throwable) throws T {
throw (T) throwable; // rely on vacuous cast
}
There is a lot of different options for handling exceptions. We use a few of them.
https://vanilla-java.github.io/2016/06/21/Reviewing-Exception-Handling.html
回答2:
Guava's Throwables.propagate()
does exactly this:
try {
// some code that can throw both checked and runtime exception
} catch (Exception e) {
throw Throwables.propagate(e);
}
UPDATE: This method is now deprecated. See this page for a detailed explanation.
回答3:
Not really.
If you do this a lot, you could tuck it away into a helper method.
static RuntimeException unchecked(Throwable t){
if (t instanceof RuntimeException){
return (RuntimeException) t;
} else if (t instanceof Error) { // if you don't want to wrap those
throw (Error) t;
} else {
return new RuntimeException(t);
}
}
try{
// ..
}
catch (Exception e){
throw unchecked(e);
}
回答4:
I have a specially compiled .class file containing the following:
public class Thrower {
public static void Throw(java.lang.Throwable t) {
throw t;
}
}
It just works. The java compiler would normally refuse to compile this, but the bytecode verifier doesn't care at all.
The class is used similar to Peter Lawrey's answer:
try {
// some code that can throw both checked and runtime exception
} catch (Exception e) {
Thrower.Throw(e);
}
回答5:
You can rewrite the same using instanceof operator
try {
// some code that can throw both checked and runtime exception
} catch (Exception e) {
if (e instanceof RuntimeException) {
throw e;
} else {
throw new RuntimeException(e);
}
}
However, your solution looks better.
回答6:
The problem is that Exception
is too broad. You should know exactly what the possible checked exceptions are.
try {
// code that throws checked and unchecked exceptions
} catch (IOException | SomeOtherException ex) {
throw new RuntimeException(ex);
}
The reasons why this wouldn't work reveal deeper problems that should be addressed instead:
If a method declares that it throws Exception
then it is being too broad. Knowing that "something can go wrong" with no further information is of no use to a caller. The method should be using specific exception classes in a meaningful hierarchy, or using unchecked exceptions if appropriate.
If a method throws too many different kinds of checked exception then it is too complicated. It should either be refactored into multiple simpler methods, or the exceptions should be arranged in a sensible inheritance hierarchy, depending on the situation.
Of course there can be exceptions to the rule. Declaring a method throws Exception
can be perfectly reasonable if it's consumed by some kind of cross-cutting framework (such as JUnit or AspectJ or Spring) rather than comprising an API for others to use.
回答7:
I generally use the same type of code structure, but condense it down to one line in one of the few times a ternary operator actually makes code better:
try {
// code that can throw
}
catch (Exception e) {
throw (e instanceof RuntimeException) ? (RuntimeException) e : new RuntimeException(e);
}
This does not require additional methods or catch
blocks which is why I like it.