(Late edit: This question will hopefully be obsolete when Java 7 comes, because of the "final rethrow" feature which seems like it will be added.)
Quite often, I find myself in situations looking like this:
do some initialization try { do some work } catch any exception { undo initialization rethrow exception }
In C# you can do it like this:
InitializeStuff();
try
{
DoSomeWork();
}
catch
{
UndoInitialize();
throw;
}
For Java, there's no good substitution, and since the proposal for improved exception handling was cut from Java 7, it looks like it'll take at best several years until we get something like it. Thus, I decided to roll my own:
(Edit: Half a year later, final rethrow is back, or so it seems.)
public final class Rethrow {
private Rethrow() { throw new AssertionError("uninstantiable"); }
/** Rethrows t if it is an unchecked exception. */
public static void unchecked(Throwable t) {
if (t instanceof Error)
throw (Error) t;
if (t instanceof RuntimeException)
throw (RuntimeException) t;
}
/** Rethrows t if it is an unchecked exception or an instance of E. */
public static <E extends Exception> void instanceOrUnchecked(
Class<E> exceptionClass, Throwable t) throws E, Error,
RuntimeException {
Rethrow.unchecked(t);
if (exceptionClass.isInstance(t))
throw exceptionClass.cast(t);
}
}
Typical usage:
public void doStuff() throws SomeException {
initializeStuff();
try {
doSomeWork();
} catch (Throwable t) {
undoInitialize();
Rethrow.instanceOrUnchecked(SomeException.class, t);
// We shouldn't get past the above line as only unchecked or
// SomeException exceptions are thrown in the try block, but
// we don't want to risk swallowing an error, so:
throw new SomeException("Unexpected exception", t);
}
private void doSomeWork() throws SomeException { ... }
}
It's a bit wordy, catching Throwable
is usually frowned upon, I'm not really happy at using reflection just to rethrow an exception, and I always feel a bit uneasy writing "this will not happen" comments, but in practice it works well (or seems to, at least). What I wonder is:
Throwable
may have been caused by something so severe that my undoInitialize
will fail, but that's OK.)
ExceptionUtils
but that does other things.Edit:
finally
is not the droid I'm looking for. I'm only interested to do stuff when an exception is thrown.Throwable
is a big no-no, but I think it's the lesser evil here compared to having three catch clauses (for Error
, RuntimeException
and SomeException
, respectively) with identical code.try
block will continue to bubble up through the call stack as soon as I've rewinded a few things.Catch and rethrow exception Medium Catching and re-throwing an exception without further actions is redundant and wasteful. Instead, it is recommended to re-throw custom exception type and/or log trace for debugging.
Re-throwing an exception means calling the throw statement without an exception object, inside a catch block. It can only be used inside a catch block.
There are a couple of way to handle this. The first is my preference if you don't need to know what the exception was.
boolean okay = false;
try {
// do some work which might throw an exception
okay = true;
} finally {
if (!okay) // do some clean up.
}
In some cases you can do the same without an extra variable, depending on what the try block does.
A second option is a hack but also works.
try {
// do some work which might throw an exception
} catch (Throwable t) {
// do something with t.
Thread.currentThread().stop(t);
}
The stop(Throwable t) method doesn't stop the thread, instead it causes the thread to throw the exception provided in an unchecked way.
You can use Unsafe.throwException() with a bit of fiddling and there is a way to do this with Generics which I have forgotten.
If you are that concerned about getting your uninitialization to happen then you may want to just put that code into a finally block, as, if it should be called at some point, then you perhaps should always clean up.
I am leery of catching Throwable
as some of the exceptions I want to handle, and some I just log, as, there is no use passing exceptions that the user can't do anything about, such as a NullPointerException
.
But, you didn't show what SomeException
is defined as, but if an OutOfMemoryException
is thrown, your throwable will catch it, but it may not be the same type as SomeException
so your wrapper will be needed in your sample function, at least when I look at the instanceOrUnchecked
method.
You may want to write a unit test, try different classes of Exceptions and see what does or doesn't work as expected, so you can document the expected behavior.
An alternative is to have a factory which creates SomeException only if the cause is a checked exception:
public static SomeException throwException(String message, Throwable cause) throws SomeException {
unchecked(cause); //calls the method you defined in the question.
throw new SomeException(message, cause);
}
The reason why I put in the return value in the method is so that the client can do something like this:
catch (Throwable e) {
undoInitialize();
throw SomeException.throwException("message", e);
}
so that the compiler is fooled into not requiring a return after the catch statement if the method has a return type, but it still throws the exception if the client forgot to put the throw before the call to the factory method.
The disadvantage of this over your code is that it is less portable (it works for SomeException, but not for SomeOtherException), but that may be ok, because it won't be for every exception type that you need to have an undo initialize.
If it fits your use case you could put the unchecked call in the constructor of SomeException and have the logic available to all subclasses, but that would have to fit your specific project - it would not be a good idea in the general case as it would prevent wrapping runtime exceptions.
public SomeException(message, cause) {
super(message, unchecked(cause));
}
private static Throwable unchecked(Throwable cause) {
if (cause instanceof Error) throw (Error) cause;
if (cause instanceof RuntimeException) throw (RuntimeException) cause;
return cause;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With