Throwing an Exception using Unsafe.getUnsafe().throwException
I came across some interesting code in java.lang.Class#newInstance0:
// Run constructor
try {
return tmpConstructor.newInstance((Object[])null);
} catch (InvocationTargetException e) {
Unsafe.getUnsafe().throwException(e.getTargetException());
// Not reached
return null;
}
Check out the Unsafe.getUnsafe().throwException
statement. It looks like a checked exception is being thrown from a method which doesn开发者_如何学运维't declare it to be thrown!
Why did they do this?
If the Sun developers can use this technique, is it ok for us to do so as well?With Java and generics, you can profit from type erasure and just throw any exception with this simple trick:
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;
}
}
Kinda scary, though...
Why did they do this?
I'm not entirely sure, but I expect there was a really good reason. (When you drill down through the layers, reflective invocation involves some extremely tricky things ...)
If the Sun developers can use this technique, is it ok for us to do so as well?
I'd say no ... unless you have a really, really good reason. Methods that throw checked exceptions without declaring them are violating the principle of least surprise. Think of the poor guy who might have to debug your code in a critical production system at 3am.
Just cos' some Sun engineer decided something was a good idea in a particular context, doesn't necessarily make it a good idea in general.
There is an interesting blog on Avoiding Checked Exeptions by Don Schwarz. Thanks for sharing.
The problem with using the Unsafe class is that it is for internal use only, and doesn't have a defined interface and IS different for different platforms.
You can achieve the same thing using standard calls. (Though deprecated)
Thread.currentThread().stop(checkedException);
Sun appararently did this to allow checked exceptions to be thrown directly from Class.newInstance()
, rather then either being wrapped in InvocationTargetException or having throws Exception
declared on the signature.
This is an "interesting" design decision as it differs from patterns elsewhere -- Constructor.newInstance() for example will wrap any constructor exceptions in an InvocationTargetException.
The "benefit" it supposedly provides is:
the ability to catch constructor exceptions directly, at their original type; and
avoids newInstance() needing to declare
throws Exception
and the caller to catch Exception.
The downside is:
- the checked exceptions are not declared. This obviates the main advantage/ purpose of checked exceptions, and may produce warnings that the catch block is unreachable due to the checked exception not being declared.
Would this be a pattern you might use in your code? To me this seems fairly unlikely. It seems mostly potentially applicable to generic/ framework code, where client code being wrapped by the framework potentially throws checked exceptions. Then it would only be desirable to use, if you wished to propagate checked exceptions out without declaring they can be thrown.. For me this is questionable.
Most Java framework methods wrapping client code, are designed in one of three exception paradigms:
- purely runtime exceptions, or
- declaring
throws Exception
to allow the full gamut of checked exceptions, or - the 'rethrow in a wrapper' approach (consider using an unchecked exception as the wrapper, though).
This particular Class.newInstance
method is very unusual, and by not declaring the checked exceptions which can potentially be thrown it bypasses/ obviates the only real benefit of checked exceptions.
Generally, there has been a major shift towards runtime rather than checked exceptions, and I would not recommend Unsafe.throwException()
(nor the "generics hack" mentioned by Lukas above.)
For application code, I would recommend to use runtime exceptions if possible & declare the exception types thrown. For framework code, consider the three paradigms above with a preference towards runtime exceptions.
Background: http://literatejava.com/exceptions/checked-exceptions-javas-biggest-mistake/
精彩评论