Hacker News new | ask | show | jobs
by default-kramer 330 days ago
I think checked exceptions were maligned because they were overused. I like that Java supports both checked and unchecked exceptions. But IMO checked exceptions should only be used for what Eric Lippert calls "exogenous" exceptions [1]; and even then most of them should probably be converted to an unchecked exception once they leave the library code that throws them. For example, it's always possible that your DB could go offline at any time, but you probably don't want "throws SQLException" polluting the type signature all the way up the call stack. You'd rather have code assuming all SQL statements are going to succeed, and if they don't your top-level catch-all can log it and return HTTP 500.

[1] https://ericlippert.com/2008/09/10/vexing-exceptions/

4 comments

Put another way: errors tend to either be handled "close by" or "far away", but rarely "in the middle".

So Java's checked exceptions force you to write verbose and pointless code in all the wrong places (the "in the middle" code that can't handle and doesn't care about the exception).

> So Java's checked exceptions force you to write verbose and pointless code in all the wrong places (the "in the middle" code that can't handle and doesn't care about the exception).

It doesn't, you can just declare that the function throws these as well, you don't have to handle it directly.

It pollutes type signatures. If some method deep down the call stack changes its implementation details from throwing exception A you don't care about to throwing exception B you also don't care about, you also have to change the type of `throws` annotation on your method.

This is annoying enough to deal with in concrete code, but interfaces make it a nightmare.

You mean like using Result with a long list of possible errors, thus having crates that handle this magically with macros?
Yes, the exact same problem is present in languages where "errors are just values".

To solve this, Rust does allow you to just Box<dyn Error> (or equivalents like anyhow). And Go has the Error interface. People who list out all concrete error types are just masochists.

Go as usual, got this clever idea to use strings and having people parse error messages.

It took until version 1.13 to have something better, and even now too many people still do errors.New("....."), because so is Go world.

It's fine to let exceptions percolate to the top of the call stack but even then you likely want to inform the user or at least log it in your backend why the request was unsuccessful. Checked exceptions force both the handling of exceptions and the type checking if they are used as intended. It's not a problem if somewhere along the call chain an SQLException gets converted to "user not permitted to insert this data" exception. This is how it was always meant to work. What I don't recommend is defaulting to RuntimeException and derivatives for those business level exceptions. They should still be checked and have their own types which at least encourages some discipline when handling and logging them up the call stack.
In my experience, the top level exception handler will catch all incl Throwable, and then inspect the exception class and any nested exception classes for things like SQL error or MyPermissionsException etc and return the politically correct error to the end user. And if the exception isn’t in a whitelist of ones we don’t need to log, we log it to our application log.
Sometimes I feel like I actually wouldn't mind having any function touching the database tagged as such. But checked exceptions are such a pita to deal with that I tend to not bother.
>you probably don't want "throws SQLException" polluting the type signature all the way up the call stack

A problem easily solved by writing business logic in pure java code without any IO and handling the exceptions gracefully at the boundary.