Checked exception

From APIDesign

(Difference between revisions)
Jump to: navigation, search
Line 8: Line 8:
There is however one more example: Imagine an exception that needs to be caught when thrown from certain methods, but when it is thrown from other methods, it should behave as as {{JDK|java/lang|RuntimeException}} - e.g. propagate silently. We have seen an example of this recently in our [[Truffle]] project. The {{Truffle|com/oracle/truffle/api/interop|InteropException}} should smoothly propagate through many calls, but if invoked via the {{Truffle|com/oracle/truffle/api/interop|ForeignAccess}}'s '''send''' method, we want every caller to handle it. What are our [[API]] design options?
There is however one more example: Imagine an exception that needs to be caught when thrown from certain methods, but when it is thrown from other methods, it should behave as as {{JDK|java/lang|RuntimeException}} - e.g. propagate silently. We have seen an example of this recently in our [[Truffle]] project. The {{Truffle|com/oracle/truffle/api/interop|InteropException}} should smoothly propagate through many calls, but if invoked via the {{Truffle|com/oracle/truffle/api/interop|ForeignAccess}}'s '''send''' method, we want every caller to handle it. What are our [[API]] design options?
 +
=== Duplicate the Exceptions ===
 +
 +
We could have one {{JDK|java/lang|RuntimeException}} subclass and one {{JDK|java/lang|Exception}} subclass. Sometimes that might work, but in this case there are four subclasses of {{Truffle|com/oracle/truffle/api/interop|InteropException}} currently (and the number is expected to grow in the future), and having a duplicated set of classes is clearly annoying. Such solution would only support the argument of some that [[checked exception]]s are the worst invention ever!
 +
 +
=== Wrap the Exceptions ===
 +
 +
We could introduce one {{JDK|java/lang|RuntimeException}} subclass and let it ''carry'' the real checked {{Truffle|com/oracle/truffle/api/interop|InteropException}} exception. That would probably work on the catch-side:
 +
 +
<source lang="java">
 +
try {
 +
// some interop code
 +
} catch (OurInteropRuntimeException ex) {
 +
throw (InteropException)ex.getCause();
 +
}
 +
</source>
 +
 +
and even on the throw side the code wouldn't be that bad:
 +
 +
<source lang="java">
 +
throw new OurInteropRuntimeException(new InteropException());
 +
// which could be simplified and hidden into a factory method:
 +
throw InteropException.raise();
 +
</source>
 +
 +
However this again suffers from the duality of exceptions. In situations where needs to be sure, one needs to catch both exceptions '''OutInteropRuntimeException''' as well as {{Truffle|com/oracle/truffle/api/interop|InteropException}}, which is again a reason for few to claim that [[checked exception]]s are bad.
 +
 +
=== Unchecking Checked Exception ===
 +
 +
Luckily, if one knows the difference between [[source compatibility]] and [[binary compatibility]] one can realize that the [[JVM]] doesn't know anything about the difference between {{JDK|java/lang|RuntimeException}} and checked {{JDK|java/lang|Exception}} - it is all just a [[Java]] language construct. Other languages built on top of [[JVM]] may ignore it. And that is what we decided to do.
 +
 +
We designed a [[checked exception]] {{Truffle|com/oracle/truffle/api/interop|InteropException}} (and its subclasses) and added a '''raise''' method to throw the exception as unchecked one. The usage is simple:
 +
 +
<source lang="java">
 +
throw {{Truffle|com/oracle/truffle/api/interop|InteropException}}.raise();
 +
</source>
[[TBD]]
[[TBD]]

Revision as of 09:49, 4 April 2016

Checked exceptions are Java invention and many like to argue that they are the worst invention ever. I like exceptions and I like Checked exceptions. One day I'll explain why.

There is a really nice thing on checked exceptions: if a method declares that it throws a checked exception, the caller of the method has to handle it. This is a really nice language feature, if used at the appropriate place. What is such appropriate place? If one reads a file one shall be ready for an input/output error - e.g. forcing people to catch IOException seems like the right thing to do.

Thus in certain situations having checked exceptions is beneficial. On the other hand, throwing checked exceptions in cases where the recovery is unlikely - a frequently mentioned example is ParserConfigurationException - is just going to pollute the client code with useless catch statements.


There is however one more example: Imagine an exception that needs to be caught when thrown from certain methods, but when it is thrown from other methods, it should behave as as RuntimeException - e.g. propagate silently. We have seen an example of this recently in our Truffle project. The InteropException should smoothly propagate through many calls, but if invoked via the ForeignAccess's send method, we want every caller to handle it. What are our API design options?

Duplicate the Exceptions

We could have one RuntimeException subclass and one Exception subclass. Sometimes that might work, but in this case there are four subclasses of InteropException currently (and the number is expected to grow in the future), and having a duplicated set of classes is clearly annoying. Such solution would only support the argument of some that checked exceptions are the worst invention ever!

Wrap the Exceptions

We could introduce one RuntimeException subclass and let it carry the real checked InteropException exception. That would probably work on the catch-side:

try {
   // some interop code
} catch (OurInteropRuntimeException ex) {
  throw (InteropException)ex.getCause();
}

and even on the throw side the code wouldn't be that bad:

throw new OurInteropRuntimeException(new InteropException());
// which could be simplified and hidden into a factory method:
throw InteropException.raise();

However this again suffers from the duality of exceptions. In situations where needs to be sure, one needs to catch both exceptions OutInteropRuntimeException as well as InteropException, which is again a reason for few to claim that checked exceptions are bad.

Unchecking Checked Exception

Luckily, if one knows the difference between source compatibility and binary compatibility one can realize that the JVM doesn't know anything about the difference between RuntimeException and checked Exception - it is all just a Java language construct. Other languages built on top of JVM may ignore it. And that is what we decided to do.

We designed a checked exception InteropException (and its subclasses) and added a raise method to throw the exception as unchecked one. The usage is simple:

throw {{Truffle|com/oracle/truffle/api/interop|InteropException}}.raise();


TBD

Personal tools
buy