2

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?

john16384
  • 7,800
  • 2
  • 30
  • 44
  • What does "Exceptions should only be checked when they're out of control of both the caller and receiver" mean? – Robert Harvey Nov 12 '21 at 15:25
  • Perhaps that is worded a bit poorly. It means you should declare an exception as checked only if callers can't prevent the exceptional situation from happening, and neither could the code that throws it. If callers can easily prevent the problem (by passing in correct parameters for example) then it shouldn't be checked. If throwers can easily handle the case, then they shouldn't be throwing anything at all. An `IOException` is something that is outside the control of both the caller and the throwing code because external systems are involved. – john16384 Nov 12 '21 at 16:18
  • Hmm... Lets check that assumption (no pun intended): [FileNotFoundException](https://docs.oracle.com/javase/9/docs/api/java/io/FileNotFoundException.html) is a checked exception, even though it could easily be solved by providing the correct file name. – Robert Harvey Nov 12 '21 at 16:53
  • Perhaps your logic is backwards. [This](https://www.baeldung.com/java-checked-unchecked-exceptions) states: *“If a client can reasonably be expected to recover from an exception, make it a checked exception. If a client cannot do anything to recover from the exception, make it an unchecked exception.”* – Robert Harvey Nov 12 '21 at 17:24
  • Perhaps you may want to choose a different venue for this discussion, I've already made up my mind on this. – john16384 Nov 12 '21 at 21:02

0 Answers0