4 ms·
To me one of the fundamental issues with checked exceptions is that they break encapsulation. There's no way around it: if foo calls bar, and bar calls readFi
by akeefer 17y ago
To me one of the fundamental issues with checked exceptions is that they break encapsulation. There's no way around it: if foo calls bar, and bar calls readFile which throws an IOException, assuming bar is unable to handle the exception itself (which is generally the case) and you want it to be reported up the stack (say, all the way to the web request handler), you have two options: you declare every method in the chain as throwing an IOException, thus polluting a ton of higher-level code with information that should be restricted to a lower layer, or you catch it and wrap it in a runtime exception, defeating the purpose of having that be a checked exception.
I think it's really just a fundamental problem with the whole idea, and it's why any attempt to "fix" them will fail: the idea of checked exceptions is just deeply flawed.
- adingwall 17y agoI often wrap checked exceptions in lower level code in higher level checked exceptions. So if I was reading a configuration file I would throw something like ConfigurationReadException and then wrap the IOException in this. Then if I switch to reading the configuration file from a database (for example), I can wrap an SQLException instead of the IOException. Encapsulation isn't broken and I still have checked exceptions.
- amalcon 17y agoCouldn't we just have the compiler automatically figure out this propagation, and then toss a warning if one could ever reach the top level of a thread? I mean, it seems like a much easier problem than ML-style type inference.
- lucifer 17y agocatch (IOException ioe) { throw new MyObjectXYZContractException ("Can't do XYZ", ioe); }
- efsavage 17y agoThe checked exception didn't break encapsulation here, you did. You have alot more than two options, and the two you listed aren't very good ones. If foo calls bar, something might go wrong, and foo should take some kind of action. If bar can't complete because of an IOException, it should throw it (or wrap it and throw the wrapper). Foo now knows that bar may not complete successfully, and can do what makes sense there (throw it's own exception, retry, etc). If foo can't complete because bar didn't complete, it should tell it's caller. If it matters that the reason Foo couldn't complete was an IOException, throw it, otherwise throw a more meaningful exception.
- akeefer 17y agoLet me try to phrase the argument more clearly: if you want to pass on a checked exception via redeclaration in the "throws" clause, you've broken encapsulation because a change to the lower layer implies changes all the way up the stack. If bar() was originally calling readDatabase() that threw a SQLException, and I change it to call readFile() that throws an IOException, if I'm passing that exception up the stack I have to change bar's signature purely due to an implementation change, and so on up the stack. So a change at the lower layer has now forced me to make changes all the way through my application stack. Henceforth, encapsulation is broken by what should just be an implementation change. The only way to avoid that break of encapsulation is to catch the exception immediately in bar(), and then do something with it: either handle it, or rethrow it as something else, either a runtime exception or as a different checked exception. But that also isn't really ideal; in my experience, about 98% of the time even checked exceptions are throw-up-your-hands sort of programmer errors that you want to bounce a fair way up the stack to some more central error-handling location, or to kill the high-level operation being attempted. In other words . . . you generally just want to treat them exactly like runtime exceptions. So while the theory of type-safety around checked exceptions is nice and all, in practice if you actually use them as part of type signatures they massively couple all the layers of your application, and I find them much more annoying than useful.