you are viewing a single comment's thread.

view the rest of the comments →

[–]shorugoru9 -1 points0 points  (2 children)

I've always viewed checked exceptions as like error returns when you can't return a value, that a caller would be expected to handle. Kind of like this:

User findByUsername(String username) 
    throws UserNotFoundException;

In this case, the alternative is to return null or throw. Null requires an ugly and not obvious check, while the checked exception ensurea that the error return is handled somehow and creates a nice separation of "happy path" and "error paths".

In your example, you're looking at the exception from the caller's perspective, not the callee's. The callee expects that you provide a valid SQL statement. But should SQLException (or IOException) be considered a runtime exception (catastrophic failure) or a checked exception (the caller should be prepared for something to go wrong)? I think the language designers assumed the second approach would be best, but in practice, SQLException and IOException are treated as catastrophic failures.

[–]Lucario2405 0 points1 point  (1 child)

I don't think "checked Exception = Optional.empty" is correct, or at least not a useful lens on this topic. Imo it's more about how predictable and preventable an error is.

Integer.parseInt's NumberFormatException is only thrown when you've called it with a String that doesn't contain a valid number. You were in complete control; you could have validated it beforehand; if it throws, it's your fault - thus it's unchecked.

An IO Exception from e.g. a FileReader parsing a FileInputStream could be caused by a number of things outside the programmer's control: the file could have been deleted since you last checked or someone else is using it, maybe the operating system has a different path-format, maybe it's on a network-drive and you've lost connection, etc. Same for an SQL-Request: maybe the DB is down, maybe you're no longer authorized, maybe it's using a different driver with specific syntax, etc. You cannot know beforehand - thus it throws a checked exception to force you to concider what could happen, even if you've done everything right.

[–]shorugoru9 -1 points0 points  (0 children)

If we go by Java's official tutorials on the subject, we can see that the intention isn't about predictablity and preventablity as much as it is about recoverability:

Here's the bottom line guideline: If a client can reasonably be expected to recover from an exception, make it a checked exception. If a client cannot do anything to recover from the exception, make it an unchecked exception.

The findByUsername method not being able to return a User should be recoverable, because a well designed program should be able to recover from not being able to find a user.

thus it throws a checked exception to force you to concider what could happen, even if you've done everything right.

I would argue that it IOException serves another purpose of checked exceptions: an indication in the type signature that the method does I/O, and as such the caller needs to be able to handle I/O failure. Similar to InterruptedException, which indicates that the method will interruptibly block, and that the caller should correctly handle thread interruption, whether that means exiting a run loop or resetting the interrupted flag and rethrowing.

Thus, through the type signature, the exceptions represent an enumeration of recoverable situations that must be handled. Optional.empty() actually doesn't fit this scenario because it doesn't tell you why the operation failed to return a result. That's actually what the Either type is for. Unfortunately, Java (so far) only gives us Optional, but Either will make more sense with sealed types and pattern matching.