Tech and Media Labs
This site uses cookies to improve the user experience.




Exception Hierarchies

Jakob Jenkov
Last update: 2014-06-23

In Java and in C# exceptions can be categorized into hierarchies. The hierarchy is created by having one (or more) exception extend another exception. The first exception becomes a subclass of the second. In Java FileNotFoundException is a subclass of IOException. Here is how a custom exception looks in Java code:

public class MyException extends Exception{
    //constructors etc.
}

As you can see there isn't much to it. The advantage of exception hierarchies is that if you decide to catch (using try-catch) a certain exception in the hierarchy, then you will automatically also catch all subclasses of that exception too. In other words, you will catch all exceptions from that certain exception and down the hierarchy. In the example with FileNotFoundException, if you catch IOException which is the superclass of FileNotFoundException, you will also catch FileNotFoundException.

Multiple Catch Blocks

As you may know already it is possible to have several catch blocks for the same try-block. This is usually the case if the code inside the try-block throws more than one type of exception. But, multiple catch blocks can also be used in the case where all the exceptions thrown inside the try-block are the same type or subclasses of that type. This code illustrates that:

try{
    //call some methods that throw IOException's
} catch (FileNotFoundException e){
} catcy (IOException e){
}

Remember that the first catch-block a thrown exception matches will handle that exception. In this example all IOExceptions are being handled by the catch(IOException e) except for FileNotFoundException. The fact that FileNotFoundException is a subclass of IOException gives us the choice of either treating all IOExceptions the same, or catch some of IOExceptions subclasses individually, as is done in the code example above. If the catch(FileNotFoundException e) block is removed any FileNotFoundException will be caught by the catch(IOException e) block, since FileNotFoundException is a subclass of IOException.

Throws Clauses

If a method can throw either a certain exception A, or any subclasses of A (Asub), then it is enough to declare in the method declaration that the method throws A. It is then allowed to throw subclasses of A from the method too. Here is an example:

public void doSomething() throws IOException{
}

You are allowed to declare the subclasses in the throws clause of the method, even if you don't really need to. It can make the code easier to read and understand for the next developer to look at it. Here is an example:

public void doSomething() throws IOException, FileNotFoundException{
}

As long as the superclass of any declared exception is also declared thrown, it doesn't have any effect on the code to include the throwing of the subclass. In the example above it has no real effect that FileNotFoundException is declared thrown when IOException is also declared. When you catch IOException you also catch FileNotFoundException. It is still possible to handle the two exceptions with each their own catch-block as shown earlier, even if only the superclass is declared thrown.

Designing Exception Hierarchies

When designing exception hieararchies for an API or application it is a good idea to create a base exception for that API or application. For instance, in Mr. Persister, our Java Persistence / ORM API the base exception is called PersistenceException. This base exception makes it possible to catch and handle all exceptions thrown by Mr. Persister in the same catch block.

If you need more granularity on the exceptions thrown, for instance because you think the exceptions may be handled differently, then add new exceptions as subclasses of your API or application base exception. That way users have the choice of either catching the specific exceptions, or just the base exception. In Mr. Persister we could add a ConnectionOpenException, QueryException, UpdateException, CommitException, and ConnectionCloseException as subclasses of PersistenceException. If the users of Mr. Persister wanted to handle a ConnectionOpenException differently than a QueryException or a ConnectionCloseException, they could catch those exceptions and handle them differently. If not, the user could just catch PersistenceException and handle all exceptions uniformly.

You can subdivide the exceptions more by add more levels to the hierarchy. For instance, you may want to distinguish between an exception thrown because the URL to the database is wrong, and an exception thrown because the database is not running. In that case you could create two exceptions called DatabaseURLException and DatabaseNotRespondingException, which both extends the ConnectionOpenException. Then users can catch the two different exceptions and respond differently to them.

Summary

In this text we have seen that exception hierarchies can be created by subclassing exception classes. It is a good idea to create a base exception for your API or application, and have all other exceptions subclass this base exception. Individual subclasses makes it possible (but not obligatory) to catch and handle these individual exceptions differently. You should create individual exceptions only for errors that can actually be handled differently.

Jakob Jenkov




Copyright  Jenkov Aps
Close TOC