The mental model I have with Java unchecked exceptions it that all methods, by default, have "throws UncheckedException" at the end of it. And since the calling line of code is also a method, it doesn't need to explicitly handle it.
What would be nice is a means to mark a method as "Cannot throw exceptions of any kind", removing the unspoken "throws UncheckedException" from the signature and forcing that method to internally handle all unchecked exceptions- enforced by the compiler.
Perhaps with the coming pattern matching to Java, we'll be able to resolve this with Return value wrapping types that are One-Of (return value type, Exception type 1, Exception type 2, etc...). Then you could call the method, and pattern match the result by type.
err := foo()
if err != nil {...}
err = bar() // accidentally ignored
err = baz()
if err != nil {...}For errors, you should do error handling, which means you want the compiler to make sure you don't miss them.
For bugs, you want to let the program bomb out, either aborting entirely or stopping at some high-level boundary from which it's possible to sanely continue. In Java, unchecked exceptions are a mostly adequate mechanism for that. Rust's catch_unwind is better, because it makes stronger guarantees [1]. Erlang's approach of terminating the thread and throwing away its heap is also very good, if you can apply that.
[0] http://joeduffyblog.com/2016/02/07/the-error-model/#bugs-are...
[1] https://doc.rust-lang.org/std/panic/fn.catch_unwind.html
try:
x = json['foo']['bar'][i]
catch OOB:
handle error
than to if not (json is a dict and
'foo' in json and
json['foo'] is a dict and
...):
handle error
x = json['foo']['bar'][i]
even though in the former, unchecked exceptions are used for non-bugs.
Go's if (err != nil) { return err; } and Rust's ? both do that.
You could imagine a variant of Java where you had to write this:
Which might be better.Unchecked exception based error handling, of course, is irretrievably broken.