How can the
EncodingException part of the code below expose implementation details when void encode() does not? The first say I can fail to encode, the latter say I can encode. What's the difference?
interface Encoder {
void encode(Object o) throws EncodingException;
}For me, checked exceptions are vital to enforce proper error handling. But this can (probably) only be achieved if the exceptions fits the problem domain. For instance, throwing an
IOException in the interface above would be really really bad because it exposes details of the Encoder, e.g., that is uses the network, or whatever. On the other hand, the only thing EncodingException exposes is that an Encoder can fail to encode the provided object. That's not an implementation detail, I think, that's the Encoder being honest and not hiding it flaws.One of the most important lesson I've learnt from using exceptions is the importance of doing try-catch-wrap-throw. For example, if an implementation of
Encoder uses a method that throws IOException then the proper way of handling such exception is to catch it, wrap it in a EncodingException and throw it to the client of the Encoder. This cleans up ugly interfaces that throws many exceptions, resulting in clear description (with semantics, yeay!) of what can go wrong when a method is called. Exceptions that fits the problem domain is the key.A couple of times I've let domain exceptions inherit from each other to define that, for example, a
RangeException is a ConfigurationException. This seemed like a good idea to me at the time, however, it seldom helped the design (it didn't make it worse either, though). In fact, the only time I find it useful is when you need to distinguish between thrown exceptions in one place but handle them in the same way in a nother place. For example (where LeftException and RightException inherits from BaseException):
interface Something {
void doIt() throws LeftException, RightException;
}
class HandlesExceptionsSeparately {
void method(Something s) {
try {
s.doIt()
} catch(LeftException e) {
// handle it
} catch (RightException e) {
// handle in another way) {
}
}
}
class HandlesExceptionsTogether {
void method(Something s) {
try {
s.doIt()
} catch(BaseException e) {
// handles both
}
}
}
But as I said, this is not very common for me. Although if you are developing a library and wish the user to have the ability to handle the different error-cases separetely, then could be useful.
Well, these are some of my thoughts on checked exceptions. In summary: I think they're good stuff if done well. :)