I am writing a library in which I use checked exceptions internally as it helps to make sure that all paths handle certain exceptional cases. However, I don't want to burden the users of my library with these checked exceptions because of this rule I use:
Exceptions should only be checked when they're out of control of both the caller and receiver.
As most of the internal checked exceptions point to configuration problems or other issues that would never occur in a correct setup (and thus can be solved by the user), I wish to expose these as unchecked exceptions at my API boundaries.
Normally, I would wrap these checked exceptions in an unchecked variant and be done with it. However, in this library, I sometimes have 3 levels of exception nesting already, for example:
hs.ddif.core.inject.instantiator.CheckedInstantiationException: Exception while constructing instance via Producer: hs.ddif.core.inject.instantiator.InstantiatorTest$H hs.ddif.core.inject.instantiator.InstantiatorTest$B.createH()
(...)
Caused by: java.lang.reflect.InvocationTargetException
(...)
Caused by: java.lang.RuntimeException: can't create H
Adding another level on top (which will not really add any relevant information) makes it harder to see the underlying cause and would require instanceof
checks on getCause
if user code does want to do specific handling. I currently do this:
catch(CheckedInstantiationException e) {
throw new InstantiationException(e); // convert to runtime at API boundary
}
But what if I did this instead:
catch(CheckedInstantiationException e) {
throw new InstantiationException(e.getMessage(), e.getCause()); // convert to runtime at API boundary
}
This strips one nesting level but keeps the trace intact. The trace looks slightly different, but still includes all lines involved.
My question is, does this approach have any down sides that I'm missing?