No, that's not fine. Typically, the layer immediately wrapping the throwing code has no knowledge about semantics. It's measurement code, logging code, caching code, transaction handling code, batch explode/aggregation code etc, but nothing that could make sense of a FileNotFoundException. The layer that wraps all this infrastructure code has more info, and can make sense of individual exceptions. By wrapping the original exception, this code now has to go on a getCause() safari to find out what happened. And yes, with checked exceptions there is no way around that, and that's the reason they should go.
If you take this approach, there are two kinds of methods: those that always succeed and those that can fail. It's similar to how Go's error type works.
Java is a mess not due to the existence of checked exceptions, but because they created an unfortunate exception hierarchy and that resulted in inconsistent API design where there's no consensus about what to do.
Never handle an exception (always rethrow) unless you know that you can do something specific in that exceptional case. In most cases just rethrowing the exception is the correct thing, and many bugs come from people mishandling exceptions.
A perfect example is InterruptedException, where really bad things happen if you don’t rethrow that.
Just rethrow, it’s generally the best thing.
Which one is "default" depends on if you're writing library code, application code, or service code. Especially in services rethrow, catch all at the top, and log is common and makes sense.
Probably less appropriate for a zlib or a file not found call. You'll have to do something (unless the answer is crash ... Which it is a sometimes!)
Either way I think the one thing I hate hate hate is, losing stacktraces! Don't throw a new exception without wrapping the old one. I need that info!
This! Throw as many RuntimeExceptions as you like, so long as you include the originating exception.
People often make the very big mistake of generically catching Throwable and then rewrapping that, which would include all Error types. Those should must be rethrown without wrapping. I don’t think you meant to imply that, but it’s easy to overapply advice like this.
You're better off returning something like Rust's Result type.
My point is that there's no reason to include special syntax/concepts for checked exceptions over just using values.
Except I knew that it would always be receiving valid JSON because this code was downstream of a Kafka Streaming app whose entire purpose was to validate that the record was valid JSON that conformed to a JSON schema.
But the authors of Jackson require me to add a useless catch clause to my code. And seriously, an IOException for unparseable JSON? At least throw an UnparseableJsonException or something.
I will write catch clauses for exceptions that could reasonably be expected to occur and are recoverable from - but if an exception does occur, and you can't recover from it, what the hell is the point of a mandatory catch, log, and rethrow?
In my experience, 99% of the handling of a checked exception is to catch them, and rethrow in RuntimeException, which shows that the intent doesn't match the reality of how people are programming.
But, that said, I'm writing code on the JVM for our own internal apps - we're never going to have a desktop user facing a stack-trace. I think that when Java was developed it was intended to be far more user-facing than it is now.
1. How exceptional are exceptions?
2. Are committed to providing good language support?
If exceptions are the result of programmer error or terrible malfunction, then unchecked exceptions are fine. But in Java, exceptions are fairly common, even for common low-level stuff: read a non-existent file, parse a non-numeric string, look up a non-existent domain name. If you're going with that approach, it makes sense to type and control these.
As for language support...Java is interesting. It doesn't have unions in any of its types except for exceptions. But that makes it interesting trying to write generics, e.g. in a very functional approach. In my experience, most functional Java paradigms just don't support any user of checked exceptions and you make all exceptions unchecked.
And I think that's a very good thing.
The author, to me, misses the point. Don't rethrow the same exception. Handle it. It you want to handle it by throwing another internal unchecked exception, that's fine- it's certainly one way to handle it. But just modifying your signature so that you don't have to do the work of handling the situation is lazy and a great way to cause yourself bugs.
It leads to some top-of-the-stacktrace method having "catch(Exception e)" where there's little chance you actually know why this exception is happening, what lead to it, what it's about. Your users deserve better than that.
The alternative is that the developer doesn't know all the circumstances they must handle. They may not see a file not found error for years in production. Suddenly an unexpected exception of throwing up and we have to dig through user error reports and stack traces to figure out what happened.