Sunday, March 8, 2009

Java Exceptions

Checked exceptions... they cause me pain. At first glance they seem to provide a way for design by contract to include exceptions. However they only increase complexity and have limited usefulness for design by contract.

SNR

Firstly, they increase the "signal-to-noise ratio" for the code. The SNR for code determines how clear your code is (someone else coined that term - but try Google for it!). Anders Hejlsberg also talks about imperative vs declarative programming which is a similar concept. Anyway consider the following code snippets:

Update UI from non UI-thread in Java:

try
{
// Run the update code on the Swing thread
SwingUtilities.invokeAndWait(new Runnable()
{
@Override
public void run()
{
try
{
// Update UI value from the file system data
FileUtility f = new FileUtility();
uiComponent.setValue(f.readSomething());
}
catch (IOException e)
{
throw new IllegalStateException("Error performing file operation.", e);
}
}
}
}
catch (InterruptedException ex)
{
throw new IllegalStateException("Interrupted updating UI", ex);
}
catch (InvocationTargetException ex)
{
throw new IllegalStateException("Invocation target exception updating UI", ex");
}

Update UI from non UI-thread in C#:

private void UpdateValue()
{
// Ensure the update happens on the UI thread
if (InvokeRequired)
{
Invoke(new MethodInvoker(UpdateValue));
}
else
{
// Update UI value from the file system data
FileUtility f = new FileUtility();
uiComponent.Value = f.ReadSomething();
}
}

The UI threading code in the C# example still adds some extra noise so I like to use Postsharp to reduce it down to this:

[UIMethod]
private void UpdateValue()
{
// Update UI value from the file system data
FileUtility f = new FileUtility();
uiComponent.Value = f.ReadSomething();
}

Which seems a lot clearer to me. When you start to do more and more UI work in Swing checked exceptions start to become really annoying and useless.


Jail Break


To implement even the most basic of implementations, such as Java's List interface, checked exceptions as a tool for design by contract fall down. Consider a list that is backed by a database or a filesystem or any other implementation that throws a checked exception. The only possible implementation is to catch the checked exception and rethrow it as an unchecked exception:

public void clear()
{
try
{
backingImplementation.clear();
}
catch (CheckedBackingImplException ex)
{
throw new IllegalStateException("Error clearing underlying list.", ex);
}
}

And now you have to ask what is the point of all that code? The checked exceptions just add noise, the exception has been caught but not handled and design by contract (in terms of checked exceptions) has broken down.


Service, s'il vous plaƮt?


When the cost of checked exceptions starts to kick in (wrapping in unchecked exceptions, millions of checked exceptions) you can see code like this in response:

try
{
// Perform some action
}
catch (Exception e)
{
log.warn("Unexpected exception processing action", e);
}

The problem with this code is well documented in the .net world. Basically if you can't handle the exception then step aside.


Summary


In non-trivial situations all of the above problems compound making debugging and maintaining code harder. They add noise to the code and the usefulness as a design by contract mechanism is questionable.


Really, in Java the divide between checked exceptions and runtime exceptions should have been a red flag: some exceptions are are unexpected.  Exceptions are for exceptional circumstances and you don't always want to deal with exceptional circumstances at all levels in your code.

No comments: