6

Our library extends another (third-party) library. When we create a child class in our library, and want to throw exceptions, should those exceptions extend the exceptions from the parent class or from our own namespace? For example:

class Their_Exception extends Exception {}

class Their_Class {}
class Their_Class_Exception extends Their_Exception {}

class Our_Exception extends Exception {}

class Our_Class extends Their_Class {}
class Our_Class_Exception extends Our_Exception {} // or Their_Class_Exception?

Is it better to keep our code as above, and extend our own namespace? The way I see it, we have these alternatives:

  1. Extend our own namespace, catching all possible Their_Class_Exception cases and throwing Our_Class_Exceptions in their place (complete encapsulation, but in general could be a fair amount of rework)
  2. Extend our own namespace, but only don't catch Their_Class_Exception in Our_Class (probably means multiple catch blocks)
  3. Extend Their_Class_Exception (seems counter-intuitive, could introduce bugs by catching the wrong exceptions)

Which of these is the best approach in general, in order to keep our code consistent?

cmbuckley
  • 163
  • 5

2 Answers2

5

If there is any chance of replacing this API with another at some point in the future, go with option 1, catch and wrap their exception in your own.

Generally, the less you refer to third-party APIs in your own code, the better, unless your code is intrinsically tied to the API (eg. the JVM or .NET Framework).

If that's not the case, or if it just is too much work, then ask yourself: Is Our_Class_Exception actually an extension of Their_Class_Exception? i.e. Is it another form of the same exception.

If so then extend it (option 3), because you'll end up with double-catches everywhere if you don't. But if that is the case, why not just use their exception?

If not or if you're still not sure then play safe and extend your own namespace (option 2).

pdr
  • 53,387
  • 14
  • 137
  • 224
1

The Liskov Substitution Principle requires that overriding methods in derived classes throw only the exceptions that are thrown by the base class method.

class Base {
    void f() throws AnException { ... ; }
}

class Derived extends Base {
    void f() throws AnException /* ok */, 
                    AnotherException /* LSP violation, not allowed in Java */ {
        ...;
    }
}

class Victim {
    void g(Base b) {
        try {
             b.f();
        }
        catch (AnException e) { ... }
    }
}

Derived d = new Derived();
Victim v = new Victim();
v.g(d); // oops, AnotherException unexpectedly thrown

This is ok:

class AnotherException extends AnException { ... }
class Derived extends Base {
    void f() throws AnException {
        ...
        throw new AnotherException();
    }
}
kevin cline
  • 33,608
  • 3
  • 71
  • 142