4 ms·
Checked exceptions failed because 99 times out of 100 the exception is not recoverable, so the try/catch block is just wasting everyone's time. (In 7 years as a
by jdm2212 3y ago
Checked exceptions failed because 99 times out of 100 the exception is not recoverable, so the try/catch block is just wasting everyone's time. (In 7 years as a Java dev I can think of one time I wrote code that tried to recover from IOException instead of just making the caller retry.)
Even when the exceptions is theoretically recoverable, it has to get propagated up properly to the caller who should be handling recovery from it. But checked exceptions don't propagate sanely through executors and across RPC calls, so good luck with that.
- ratherbefuddled 3y agoThough I mostly agree it's slightly more subtle IMO. They failed because the writer of the method cannot know what the caller can recover from but must decide before compile time. The caller gets little say.
- treffer 3y agoI find the rust `?` construct nice for this: give me the success result or propagate the error. It is based on a result type though. The try/catch construct require too much code for the common propagate case. It would be nice if Java had a similar construct for error handling.
- jayd16 3y agoThe Task type in C# with the await unwrap sugar is similar to this. You can even check for and grab the exception without throwing if you want to. Still, it's too bad the error type must be a throwable. I kind of wish it could just be a plain type so you can error or cancel without generating stack traces. Awaiting a failed task could still throw. Would be a nice perf boost. As it is now, you don't want to actually cancel or fail a C# task in performance critical code. You need to successfully complete the Task and return an error, which is pretty confusing.
- amalcon 3y ago"throws IOException" is too much code? Or is the issue more that you can't really do autocoercion to a declared thrown type in Java the same way that you can do in Rust? Proliferation of types is an issue in Java, but the whole language has that problem. It's not just exceptions.
- mr_00ff00 3y agoPersonally if I were to argue why rust error handling is better than Java it wouldn’t be the too much code part. I feel the same way about Go vs Java or Zig vs Java, I think errors as values makes more sense. It’s the laws of the universe breaking and control flow changing on errors that I think most people hate.
- jdm2212 3y agoYeah, that'd be nice.
- jayd16 3y agoIs this a problem with checked exceptions or a problem with Java? Couldn't exceptions be treated as robustly as param and return types in the generic system so they are more composable? Why not a Future<V, E1> interface? The argument that some exceptions will always be runtime (like oom) so checked exceptions are flawed is harder to argue against but I would also say it's a matter of opinion whether you feel like it's worth dropping entirely.
- jdm2212 3y agoYou can return union types in Java if you want that, although it'd be nice to have better first class support for them. The issue though is that forcing people to handle irrecoverable exceptions is just kind of dumb. If I do file IO, I know it'll barf sometimes. Sometimes I care, but the vast majority of the time I don't. A lot of Java servers have RPC endpoints that basically say "write X to file Y" or "read X from file Y" and if the S3 connection is busted, there's nothing the server can do about that. Someone has to go log spelunking, figure out what's wrong (network is misconfigured, AWS is having an outage, whatever) and fix it. So it is bad API design on Java's part to make every single one of those RPC endpoints wrap every single thing that does file IO in a try/catch.
- Gibbon1 3y agoI always feel like the error domain changes as you go across program domains. I keep returning to something about error handling that bothers me. People argue what the proper way to handle errors is without really considering that it's highly context sensitive. Which makes me think you should be able to pass an error handler down to lower level functions that tells them what to do when something bad happens. Sort of like recent ideas where you pass functions a allocator instead of them calling malloc() directly or whatever.
- bluepizza 3y agoI don't think they are meant to be recoverable from, but instead they are a way to provide a controlled shut down of an irrecoverable failure, or to limit the blast radius of a localized failure. In that sense, they are quite useful. Saving the file generated an exception - instead of suddenly closing the application, display an error. Or maybe a database is not accessible anymore. Instead of suddenly ending the service, trigger a controlled shutdown (log, send alerts, etc). I don't think anyone would expect exceptions to propagate through RPC calls. A call fails, probably containing a description of the fail. Why should it propagate? There are ways to handle errors and return errored RPCs without exceptions, such as internal error codes. But error codes are just as irrecoverable as exceptions.
- jdm2212 3y agoIn a desktop application that is not allowed to totally crash, or at least has to crash kind of gracefully, checked exceptions are useful. But in the world that most Java devs live in, which is various flavors of RPC server, failing requests is fine. If lots of requests fail, your monitoring infra should page someone, and that someone will go log spelunking and figure out what's broken. Very occasionally it turns out that the thing causing the RPC failures is a recoverable exception, and then you should wrap the problematic stuff in a try/catch block. (Often you'll wind up having to detect the recoverable error case by conditioning on substrings in the exception message, which the library owners will arbitrarily change in future releases. So make sure to write regression tests so you'll catch this when you upgrade the library. Java is fun!) But 99% of the time the failure is "network is busted" or "config was invalid" or "hard disk failed" and you should not be defensively programming against all those possibilities.
- bluepizza 3y ago> you should not be defensively programming against all those possibilities This is where libraries and frameworks come into play - they defensively program against that for you. And wrap it all up in a simple interface with, well, checked exceptions.
- 3y ago