3

I have created the following classes:

  • Abstract logger class
  • Three subclasses of the abstract logger class which implement actual loggers
  • Class which acts as an interface to the logger

The three subclasses can throw exceptions (creating file, writing to file etc.).

Should I catch the exceptions directly in the three subclasses or rethrow it there and catch it in the interface? Or perhaps catching in a class which uses the logger interface?

Second, I have a class which parses a settings file. I have made it with the singleton pattern (only one instance at runtime). Of course in this class exceptions can occur (NullPointerException and IOException).

Should I catch these exceptions directly in this class or rethrow it to a client of that class?

My general problem is that I don't know when I have to catch the exceptions and when to rethrow it.

weston
  • 54,145
  • 21
  • 145
  • 203
machinery
  • 5,972
  • 12
  • 67
  • 118
  • 4
    This depends on the situation and requirement. – Stultuske Feb 03 '16 at 13:30
  • Can these loggers properly handle these exceptions? If not, then propagate them. – Tom Feb 03 '16 at 13:32
  • 1
    A corollary of Item 61 in Effective Java 2nd Ed ("Throw exceptions appropriate to the abstraction") is that you should throw the exception *if it is appropriate to the abstraction*, or catch it *if that is more appropriate to the abstraction*. It very much depends upon what you are trying to model. – Andy Turner Feb 03 '16 at 13:35

3 Answers3

3

Part of the Liskov substitution principle states:

No new exceptions should be thrown by methods of the subtype, except where those exceptions are themselves subtypes of exceptions thrown by the methods of the supertype.

This is so when substituting one type for another in the client code, any exception handling code should still function.

If you choose to use checked exceptions, Java enforces this for you. (That is not a recommendation to use checked exceptions, but I will use here to demonstrate the principle).

That's not to say you should catch all exceptions and convert. Exceptions can be unexpected (i.e. RuntimeExceptions in a checked exception environment), and you should only translate exceptions that match.

Example:

public class NotFoundException {
}

public interface Loader {
    string load() throws NotFoundException;
}

Usage:

public void clientCode(Loader loader) {
   try{
      string s = loader.load();
   catch (NotFoundException ex){
      // handle it
   }     
}

This is a good implementation that catches an exception that it makes sense to catch and translate, and propagates the rest.

public class FileLoader implements Loader {
    public string load() throws NotFoundException {
        try{
          return readAll(file);
        } catch (FileNotFoundException ex) {
          throw new NotFoundException(); // OK to translate this specific exception
        } catch (IOException ex) {
          // catch other exception types we need to and that our interface does not
          // support and wrap in runtime exception
          throw new RuntimeException(ex);
        }
    }
}

This is bad code that translates all exceptions and is not required to satisfy Liskov:

public class FileLoader implements Loader {
    public string load() throws NotFoundException {
        try{
          return readAll(file);
        } catch (Exception ex) {
          throw new NotFoundException(); //assuming it's file not found is not good
        }
    }
}
Community
  • 1
  • 1
weston
  • 54,145
  • 21
  • 145
  • 203
3

You should catch an Exception when you know how to deal with that exception.

My general problem is that I don't know when I have to catch the exceptions and when to rethrow it.

This suggests you should throws the Exception on the method as you don't know what to do with the Exception at that stages.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
2

In my opinion, exceptions should always be propagated back to the Caller. You can always catch, log and then re-throw the same or a custom exception from the class itself but ultimately it should be handled by the Caller.

For Example, If DataAccessException occurs in your DAO Class then you can catch & log this in your DAO and then re-throw a custom exception which is propagated to the Caller Class and then the Caller should decide what it needs to do with that exception.

user2004685
  • 9,548
  • 5
  • 37
  • 54