I can definitely think of situations where I'd prefer the exception to be a static inner class than merely a class in the same package.
The reasons not to do so seem to be:
- Coupling the exception to the class that throws it makes it inappropriate to reuse in other contexts
- Nobody else does it
I do not find either of those arguments at all convincing.
For the first point, why should this hypothetical future opportunity for re-use arise in the same package? This argument leads to the conclusion that we should put all exception classes as high as possible in the package hierarchy so that when we discover a future opportunity to reuse the same exception we don't have to introduce a dependency on where it was originally defined.
But even without the "taken to extremes" point, consider an exception intended to convey that class Foo
was given wrong input. If I call it Foo.InvalidInput
, the name is short and the association with Foo
is impossible to miss. If I put it outside the Foo
class and call it FooInvalidCriteria
, then I can't reuse it from class Bar
anyway, without changing its name (equivalent to changing its location).
But worst is if I leave it outside Foo
and keep its name general like InvalidInput
. Then when I later realise that Bar
might have invalid input too and make it start throwing this exception. Everything compiles and runs fine, only now all the places that were catching InvalidInput
and assuming they were handling errors from Foo
could now also be handling errors from Bar
if Foo
happens to use Bar
internally in a way that could cause this exception to be thrown. This could easily cause code breakage.
The reality is that taking an exception that was previously conceived as specifically indicating a situation that arises in one class and re-using it as a general error class is an interface change, not just an internal implementation change. To do so correctly in general you must revisit all the sites where the exception is caught and make sure they're still correct, so having the compiler tell you about all the use sites (because you have to change the name and/or import path) is a good thing. Any exception that you might make a static inner class is inappropriate for reuse in other contexts no matter whether you actually make it an inner class or not.
And as for the second dot point... "nobody else does it" never bears on anything. Either it really is the wrong thing to do, so there will be other reasons not to do it, so the "nobody else does it" argument is unnecessary. Or it isn't. And it's not like this particular example would even be terribly complicated and hard to understand, so not even the "it's unexpected so people will have trouble following it even if it's a good idea in theory" argument is very strong.