
About this episode
Java's checked exceptions are both an integral part of the language and one of its most contested features. Whether their introduction was a mistake and whether they should all be turned unchecked are frequently discussed topics but since the former is not overly relevant and the latter unlikely, this conversation isn't moving Java forward. Instead, let's talk about specific issues with checked exceptions and what could be done about them - from (entirely speculative) language changes to (marginally realistic) JDK/library evolution to stylistic changes.
Interactive timestamps
Jump to segmentGet every episode summarized
Each time Inside Java publishes, we email you a written briefing from the transcript — the topics, who appeared, and any specific claims, with the ad reads skipped.
Email me new episodesFree for 3 shows. No card needed.
Hosts & guests
Transcript ready
213 searchable segments. Every word is indexed and playable.
Full transcript
Inside Java — "Towards Better Checked Exceptions" [IJN]. Machine-transcribed; use the interactive transcript above to jump the player to any line.
0:00Hello and welcome back to the Inside Java Podcast, the podcast about everything Java brought to you by the team at Oracle that builds Java. My name is Nikolai Parlock and this is the podcast version of Inside Java Newscast number 107 towards better checked exceptions, first published to YouTube on February 19th. Enjoy the episode! So why discuss exceptions and particularly checked exceptions now? Unlike most of the episodes, this one is not triggered by a specific proposal. It's kind of the other way around, with Java evolving a lot in recent and coming years, it may well address error handling at some point. But for that conversation to be fruitful, we must move beyond checked exceptions bad, turn of please, and there are two parts to that. First, understanding that within the solutions space for error handling in Java, checked exceptions are a net positive. Yes, I like checked exceptions and more than that, I think it's not just subjective preference
1:02but YouTube objective benefits. But I won't be making that argument here, it has been made countless times before, with just as many counter-arguments and it takes up a lot of time and energy without getting us anywhere. Instead, we'll be operating from the position that checked exceptions remain an integral part of Java, because they're beneficial or otherwise because Brian gets and enjoys us suffering either way, they're here to stay. Which brings us to the second part and the conversation I actually want to have. One that I believe would be much more fruitful, namely, if checked exceptions are so great, why do so many developers dislike them? I mean, even the JDK desks were at some point so flustered that they created unchecked IO exception. So what are sharp edges and which one could open JDK realistically file off? Let's talk about actual besteps instead of just repeating the same flame war. And this also goes for the comments, by the way. Okay, with what way well I've been the longest newscast intro ever behind us, let's get going. Ready? Then let's dive right in.
2:02As you know, whether an exception is checked or not depends on where it sits in the exception type hierarchy. If it extends runtime exception, it's unchecked. If it doesn't, it's checked. And yes, will ignore errors. Unfortunately, determining this essential property by inheritance collides with other reasons to build a type hierarchy. In this case, particularly with unified error handling and access to information. A good example is SQL exception. Catching it allows handling all SQL related problems that code can be expected to encounter and it gives unified access to error codes and the SQL state. But because it's checked, saw all exceptions that extended and there are a bunch of them that really shouldn't be. For example, what's the recourse when catching SQL syntax error exception or SQL invalid authorization spec exception? In almost all situations, an SQL syntax error and validity be authentication is a bug in the program and so the exception should be unchecked. So when creating an exception type as the root for domain-specific exception hierarchy, you need to decide for all inheriting exceptions where there'll be checked or not and if you
3:05believe in the value of checked exceptions like OpenJDK does, then you are very likely to err on the site of making them all checked instead of all unchecked. All that to say, it would be great if Java could find a more detailed way to mark exceptions as checked or unchecked, one that allows us to apply that to individual types in larger hierarchy, for example through a marker interface. Because here's the thing, that checked exceptions provide value doesn't mean that they don't have downsides, which means it's important to use them diligently and only where it makes sense for the code calling a method to handle its error. Inherited check-at-ness is one reason why there are too many checked exceptions, but it's not the only one. It's just overly liberal use of them. Take input streams and output streams close methods, for example. They throw the checked IO exception, but what can you possibly do when catching them? Worse, these two classes and a bunch of their subclasses state that close doesn't even do anything, so they force you to deal with an exception they never throw because some subtype might. No wonder so many of us dislike checked exceptions.
4:07Suboptimal API design can also lead to checked exceptions being more prominent than they need to be. API is sometimes bundle functionality, while only one part throws a checked exception. But without a way to isolate that aspect, every call requires dealing with this exception. Take JDK methods like string get bytes, for example. It needs a character set, and in the methods initial form you specify the charset by name, but what if no such charset exists? That's why get bytes throws the checked unsupported encoding exception. So even if the name charset worked well for the other 10 places in your code base, you still need to handle the checked exception here. Java 6 fix this by introducing an overload that takes a charset instance. But interestingly, the static factory method charset4name does not throw a checked exception. Passing it in a legal name is considered a programming error because there's also the static method is supported, which you're supposed to use before trying to instantiate a charset. That successfully decreases the checked exception counter by one.
5:09But the longer I think about it, the less convinced the AM that it really improves anything. But that's a thought that needs more time to marinate, maybe photo for another video. For now, let's summarize a few things, libraries, the JDK, as well as others in the ecosystem, can do to improve the situation. First, they can revisit which exceptions need to be checked and consider spreading exception types if necessary. They may also be able to restructure APIs so that operations that throw checked exceptions are extracted from those that don't, giving users the chance to isolate error handling in fewer places, just like with a charset example. It may also be possible to buffer error states instead of throwing on every call, an example for this would be print stream and print wider, although these classes do have other issues. Unfortunately, there's a problem with changing an API to no longer throw a checked exception. Namely, that such a change is source incompatible. A tri-catch block that catches a specific checked exception demands that something in the tri-block declares to throw it, which makes the reduction of checked exceptions daunting, specifically for JDK APIs.
6:11And it's not like removing a handful of them would move the needle. For this to have a positive impact, a decent chunk of checked exceptions would have to turn out to be unnecessary and be removed, each of them a breaking change. So it would be really helpful for this approach if the compiler could relax a bit and let us compile code that catches checked exceptions that aren't thrown. This has its own downsides, of course, so it requires careful consideration. But it would also help with another aspect that makes checked exceptions annoying. As soon as you invoke a method that throws, you need to do something to keep the code compiling. So far so begrudgingly good. But then, when you're still in the middle of working on that piece of code and comment out a method or move things around, and nothing throws the exception anymore, you need to undo all that. For checked exceptions, constantly yell at you from the rafters while you're trying to line up your shot. Very annoying. This segment might as well be called just use either. And while that can help, I'd argue that the utility of this advice is pretty limited. This video is already running long, so I'm not introducing how either works.
7:13If you don't know, there's a link to an explanation in the description right below the like button. First of all, let's observe that a method that returns a value but can throw a checked exception is functionally the same as a method that returns something like either of that value or that exception. But handling an either versus an exception is of course different, and there are two downsides that in my opinion make either a new solution in Java. One is that exceptions are much easier to pass up the call stack if you don't want to handle them right there. Just call a bunch of methods and add the exceptions to your throws clause. With either, all those calls need to happen in a functional pipeline, and while like those more than most, I absolutely don't want all my code to be stuck in them. The other and much more relevant downside is that either removes the type information from throws into a generic type, and frankly that sucks because it means that when you chain operations that can throw different kinds of exceptions, say the first is an IO exception and the second an SQL exception, the either generic type will quickly escalate to just
8:17exception, which removes a lot of information from the type system. In most situations, this is not an improvement, but that doesn't mean that there is no room for either. Either. Check the exceptions, build on an implicit assumption of immediacy, an operation can throw an exception and because you're calling the operation you need to handle the exception. But what if you're not calling the operation, or at least not directly? In a stream pipeline for example, you are passing the operation on and something else executes it later on your behalf. Now the fact that your operation can produce an error needs to be transported from where you passed it to where you trigger the execution, for example from a stream map to its list. You could try to capture the execution types with generics, but you run to the same issue I described earlier. And the difficulty or sometimes outright impossibility of transporting the error information is why checked exceptions and deferred computation don't work well together.
9:17This often comes up as lambdas don't work to check exceptions, but that's mostly missing the point. Ping me in the comments if you want me to go into more details on that. So checked exceptions struggle with transporting type information from registering to executing a deferred operation. You know what doesn't? Either. Which is why despite its shortcomings, it's very handy in specific situations like in a stream pipeline. And I think there are two Java features that could improve this overall problem complex. Admittedly, the entirely speculative, but hear me out. First, one could be the ability of the Java compiler to track multiple checked exception types in a single generic parameter. That way in either or a beefed upstream could easily collect multiple exceptions. The language no terminology for this would be variadic generics or union types to different approaches that could achieve the similar outcomes for our use case. And the other feature could be a simple way to go from a method that returns and throws
10:18to an either of both and back. Then API's could stick to declaring return types and checked exceptions, but the users could easily switch to a form that works better for deferred computation. This could be a pure library feature, but it could also have a language component where a try turns it to expression that returns in either. Even language change in this space that deserves a mention and does have a jet draft is for switch to gain the ability to handle exceptions that were thrown by the selector expression. In situations where the error case can be corrected to yield an instance of the same type as the successful branches, this would be very useful. It's essentially try as an expression for recoverable cases. This draft is also linked in the description. Beyond the changes that could be applied to the language or to libraries, I think our style can change as well. For example, it has long been accepted that tests just throw whatever exception they encounter. If we write scripts or other small programs, we can't just do the same.
11:18For those of us who don't mind functional APIs, we can aggressively replace checked exceptions with either try or even just an optional. And in situations where nothing can be done about an error, except letting a high level handler catch it, wrapping a checked exception in an unchecked one is correct and easy to do too. For any of those options, it's straightforward to create helper methods that wrap method calls accordingly. Yeah, it's not as low effort as a make everything unchecked switch, but it's also not exactly burdensome. Or as we move towards more APIs that rely on pattern matching, we can express some error cases through domain-specific return types. Either way, teams absolutely should get together and create a style guide for their project that matches their domain and preference. But in the end, I think none of these variants, nor the failure mechanisms of other languages, by the way, will be easy to work with if you want to do more than just let errors rip. Because good error handling is hard inherently. Of course, different mechanisms have different trade-offs, and I genuinely believe that some
12:19can, in aggregate, be better than others. But that doesn't change the fact that a big chunk of the complexity is essential. An example is the general difficulty to communicate information across tech frames and abstractions. Something that plays logging too, by the way. An unfortunate consequence of that is that there's no Pareto principle at play, where a few small or easy improvements will yield large results, and anybody claiming that probably has not thought about the problem for very long. Instead, it seems that a lot of hard work is required to move the needle. So let's talk about and tackle that hard work. Let me know in the comments what changes you think would make checked exceptions more usable. Other than that, I'll see you again in two weeks. I need to hurry up and wait to catch my train. So long.
More episodes
More from Inside Java

"Make Java Safer with Flexible Constructor Bodies" [IJN]
Inside Java

"Ask the Architects at JavaOne'26" [AtA]
Inside Java

"You Must Avoid Final Field Mutation" [IJN]
Inside Java

"How JDK 26 Improves G1's Throughput" [AtA]
Inside Java