Gemini doesn’t understand exceptions. Then again neither do you.
Some days I feel like code review is an exercise in educating developers. Nowhere is that clearer than in Java exception handling. Almost nobody understands this. When I’m interviewing software engineers, I pretty much never ask about exceptions because it’s an almost 100% guaranteed fail. If I’m being interviewed and I’m asked about exceptions, I have to play the meta game where I assume that my interviewer believes things that aren’t true, and I tailor my response to fit their misconceptions. Mostly I do that by focusing on the syntax and behavior without covering the semantics and proper use of exceptions. Effective Java comes closer than most to correctly explaining which exceptions you should use when, but even Bloch doesn’t quite stick the landing.
Today after yet another round of back-and-forth code review comments where I once again had to explain to an otherwise experienced developer how exception handling is supposed to work, I got the bright idea to ask Gemini, Google’s machine learning chatbot, what it thought. It gave me a better answer to the question than most programmers do; but it still made two fundamental mistakes, one of commission (saying something that isn’t true) and one of omission (leaving out an absolutely critical point). See if you can spot the mistakes.
My prompt was:
In Java, which error conditions should result in a runtime exception and which error conditions should result in a checked exception?
And here’s what Gemini answered:
In Java, the decision of whether to use a checked or unchecked (runtime) exception hinges on how the calling code is expected to handle the error condition. Here’s a breakdown of the general principles:
Checked Exceptions:
Use for: Conditions that a reasonable calling method should be able to recover from. These represent situations that are often predictable and external to the normal flow of the program.
Characteristics: The compiler forces the calling method to either
catch
the exception or declare that itthrows
it. This makes the possibility of the exception explicit and encourages proper handling.Examples:
IOException
(e.g., file not found, network issues): A program might retry the operation, inform the user, or choose a different source.SQLException
(database errors): A program might retry the query, handle specific error codes, or display an error message.ClassNotFoundException
: The program might try a different class loader or inform the user that a required library is missing.- Custom exceptions representing business logic errors that the caller is expected to handle (e.g.,
InsufficientFundsException
).When to use: If the error is something that a typical calling method could reasonably be expected to handle (even if that handling is just logging and exiting), a checked exception is usually the right choice. It makes the code more robust and less prone to unexpected crashes.
Unchecked Exceptions (Runtime Exceptions):
Use for: Conditions that are typically the result of programming errors or truly exceptional circumstances that a reasonable calling method usually cannot recover from. These often represent internal errors or violations of program logic.
Characteristics: The compiler does not force the calling method to handle these exceptions. They can be caught, but it’s not required. They often indicate a bug in the code.
Examples:
NullPointerException
: Dereferencing anull
object. This almost always indicates a programming error.IllegalArgumentException
: A method receives an argument that is outside the allowed range. This usually means the caller’s logic is flawed.IndexOutOfBoundsException
: Trying to access an array element with an invalid index. Again, a programming error.ArithmeticException
(e.g., division by zero): Often (but not always) a programming error.RuntimeException
and its subclasses: This is the base class for unchecked exceptions. You can create your own custom runtime exceptions for situations that are truly unrecoverable or indicate a severe program fault.Error
and its subclasses (e.g.,OutOfMemoryError
,StackOverflowError
): These represent severe problems with the JVM itself and are generally not something you would catch.When to use: If the error is something that almost always indicates a bug in the code, or a condition from which the program is unlikely to recover, an unchecked exception is usually more appropriate. Forcing callers to handle these can lead to overly complex and often meaningless
catch
blocks.
One thing I will note in Gemini’s favor. Unlike most programmers, it can write. Words are spelled correctly. The sentences are grammatical. The information, even when it’s wrong, is presented in straight-forward, easy-to-understand prose. It’s not Hemingway, but it’s better technical writing than most college graduates can produce on their own. But it is wrong.
Gemini’s answer is, however, better than what 99% of senior Java developers will tell you when asked this same question. In particular, it correctly notes that runtime exceptions are the result of “Conditions that are typically the result of programming errors…. These often represent internal errors or violations of program logic.” In other words, a runtime exception is the result of a bug in the program. This is a critical point most developers forget. If a runtime exception does happen, then there’s a bug in the program, and it should be fixed. Once it is fixed, that exception no longer happens. Conversely, if the exception is not caused by a bug in the program, it should not be a runtime exception. Hold on to that thought. I’m coming back to it.
But notice those ellipses. They’re hiding the part Gemini gets wrong. Specifically, conditions that are “truly exceptional circumstances that a reasonable calling method usually cannot recover from.”
This is wrong in at least three ways. First, and least controversially, the calling method never needs to recover from an exception. There are many ways to handle an exception that are not reasonably considered to be recovering. Most importantly, the calling method can simply let the exception bubble up to the method that called it. Very often that’s what it should do. (To answer a common objection, what if the calling method doesn’t declare that it throws that checked exception? Then change the damn signature. It’s just code. I delve deeper into this point in “When you can’t throw an exception: working with exception-free superclasses“.)
What Gemini should have said instead is, “conditions that are truly exceptional circumstances that a reasonable program usually cannot recover from.” But that’s wrong too. Truly exceptional circumstances that a reasonable program cannot be expected to recover from are errors, not exceptions. This includes OutOfMemoryError
, NoClassDefFoundError
, and the like. They do not include a file that isn’t where it’s supposed to be, a malformed XML document, a full disk, or a misbehaving network server.
And this brings us to Gemini’s error of omission. It never indicates that checked exceptions are for conditions that arise from circumstances outside the control of the program itself. Any problem that comes about because of something other than the program itself is not a bug. It’s an expected circumstance that must be handled by robust code. Letting the program die with a stack dump is not an adequate response. Even if an environmental problem is so bad that the program must shut down, the program should still shut down cleanly in an error handler that closes all open resources and alerts the operator to a serious problem they need to look into. It’s not OK to just quit.
And this brings us to the correct answer that Gemini (and almost everyone else) doesn’t know:
Runtime exceptions are for conditions that indicate a bug in the program itself. Checked exceptions are for problematic conditions in the environment the programs runs in.
That’s it. That’s the whole answer. And perhaps if Gemini were truly intelligent instead of simply parroting what it copied from a thousand different incorrect sources, it would have figured that out.
Next week: See Gemini give an even worse answer to a basic question about XML.