I've heard that catching java.lang.Error
is considered bad practice.
I'm currently loading a .dll that is not guaranteed to be on the PATH, and would like to switch to a user-configured location in the case that it isn't.
try {
System.loadLibrary("HelloWorld");
} catch(UnsatisfiedLinkError ule){
System.load("C:/libraries/HelloWorld.dll");
}
Is there a better way of doing this? Or is catching the UnsatisfiedLinkError
here acceptable?
Other than giving advice on how to technically overcome the problem, I'd like to take a moment and explain why it's considered "bad practice" in the first place.
Let's start off by clarifying what the Error
class is.
In java, errors and exceptions (which are the main types) are thrown. Throwing one of the above is done by using the throw
keyword. Every class which extends the basic java.lang.Throwable
can be thrown.
There are two classes which inherit from the basic Throwable
class: Exception
and Error
. The difference between those two is explained in their documentations:
An Error is a subclass of Throwable that indicates serious
problems that a reasonable application should not try to catch. Most
such errors are abnormal conditions. [...]
Source
The class Exception and its subclasses are a form of Throwable
that indicates conditions that a reasonable application might want
to catch.
Source
As explained above, errors and exceptions are separated because of their different origins. An Error
normally indicates a problem, which the application can not recover from. Therefore, they should not be caught.
The same is true for a RuntimeException
, but it is used to indicate a problem with a high-level layer (e.g. methods). Whereas the Error
indicates a low-level problem (e.g. the runtime).
So, now that you understood that you shall only catch exceptions and errors which you are able to recover from, the answer to your question should be clear.
Yes, it's perfectly reasonable to catch the UnsatisfiedLinkError
, because your application can recover from it.
I covered the above (in more detail and with examples) and some extended information in an article on my Blog.
You should only catch Errors in very specific cases. Only catch and error if you have explored all other possibilities. I completely agree with everything Lukas Knuth said. But i have one small addition.
In case you to catch any kind of error, make sure that you catch errors from as narrow a scope as you can. Also, if possible, make sure that the methods you catch errors on are declared as final. The reason is that catching Errors can usually lead to some very shaky programs. Consider that you catch an error on a method that is later extended to call other methods, all these underlying methods would now also have errors caught (unintentionally) by the overlying catch.
If you need to catch an Error, do it in a narrow, controlled fasion.
loadLibrary calls findLibrary() which would be helpful but it's protected, your best bet is to write your own class extending ClassLoader. Class loader has a protected method called findLibrary() which will return the path to a library or null if it doesn't exists. That way you can just check for null instead of catching errors. I'm not sure if this is actually "better" but it will remove your need for try catch;
If you are coding defensively and can recover from an issue, then it's not a Java Error
. If such an issue is not very likely, then create a subclass of Exception
and throw and catch that. If such an issue is likely, then it shouldn't even throw an Exception
; but, should be part of the regular code flow.
try {
if (config.hasCustomDLL()) {
System.load(config.getCustomDLL());
} else {
System.loadLibrary(Config.DEFAULT_DLL);
}
} catch (UnstatisfiedLinkError e) {
System.out.println("Error loading DLL: " + e);
}
Errors
are meant for really bad failures, not recoverable "failures" which really aren't even failures if there is a suitable workaround. Don't overload the system designed to handle failure with what amounts to an ability to configure the system multiple ways.