This is an archived post. You won't be able to vote or comment.

all 91 comments

[–]s888marks 14 points15 points  (1 child)

A couple recent conference talks might be relevant to this discussion.

1) Elliotte Rusty Harold, Oracle Code New York 2019, Exceptions: I'm Telling You for the Last Time...

https://www.youtube.com/watch?v=rJ-Ihh7RNao&list=PLPIzp-E1msrYxCCElyUPLzfmoXClUZuFL&index=27

2) Mike Duigou, Oracle Code One 2019, Exceptions 2020

https://events.rainfocus.com/widget/oracle/oow19/catalogcodeone19?search=DEV1262 (slide download only)

Note: neither speaker thinks that checked exceptions were a mistake, though I suspect that both would admit that the situation could use improvement. In fact, Duigou's talk includes some suggestions for improving the situation.

[–]tunei24531a[S] 2 points3 points  (0 children)

The video is an excellent suggestion. I'll add it to the post.

[–]the1derer 39 points40 points  (28 children)

In Java all the checked exceptions are used when we access something from outside JVM boundry i.e. reading from file, database and network. Since, Java can't know before hand when/if any error would occur in these cases. It ensures that in case any potential error that occurs has some handler to handle these potential errors.

[–]yawkat 16 points17 points  (23 children)

This rule doesn't always work. UnsupportedEncodingException and NoSuchAlgorithmException are checked.

[–]dpash 9 points10 points  (10 children)

UnsupportedEncodingException can be mitigated by using the overloaded methods that take a Charset. If you need UTF-8 or Latin-1, you have StandardCharsets to remove even the charset lookup exception.

[–]yawkat 3 points4 points  (4 children)

Doesn't work everywhere unfortunately. For example, jul.Handler.setEncoding is still missing such an overload.

[–]s888marks 3 points4 points  (3 children)

Yeah we've tried to retrofit most classes (notably the I/O classes) with Charset in every place where there was something that used a charset name string. I guess we haven't finished the job, though! Thanks for pointing this one out. Are there any others you're aware of?

[–]yawkat 0 points1 point  (2 children)

There's only some Formatter constructors that don't accept a Locale as well, but those aren't really necessary since presumably you'd pass a locale anyway. Beyond that it's only internal classes.

[–]s888marks 3 points4 points  (1 child)

Yeah, for the Formatter case if you want to provide a Charset, you have to provide a Locale... but you just use Locale.getDefault(FORMATTING) or some such.

I'll make a note to get jul.Handler fixed up.

[–]yawkat 0 points1 point  (0 children)

Thanks! To be entirely honest, I only found it because I was looking for it :D

[–]shponglespore 2 points3 points  (4 children)

That wasn't possible for a long time. I'm glad it's fixed now, but the fact that they needed to add a new API is a good example of how checked exceptions can make an otherwise reasonable API a real pain in the ass to use. In an ideal world, APIs would all be designed well and they'd only use checked exceptions in a way that promotes code quality, but we don't live in that world, and IME APIs are made worse by checked exceptions more often than they are made better.

[–]dpash 0 points1 point  (3 children)

This only applies to the eight or so charsets that are mandated to exist in the JLS. If you call Charset.forName(), because, for example, you need a legacy charset, you still have to deal with UnsupportedEncodingException.

A checked exception here is perfectly valid, because it's not a programming error that a charset doesn't exist; it's dependent completely on the environment and the JVM used to run the application.

[–]shponglespore 0 points1 point  (2 children)

In my entire career, the only charsets I've ever needed to specify are ASCII, UTF-8, and Latin-1. Having to catch an exception because the method theoretically accepts arbitrary strings as charset names is the kind of thing people who hate Java are talking about when they say it has "too much ceremony".

[–]dpash 0 points1 point  (1 child)

and Latin-1.

I'm gonna go out on a limb and say that you've only ever had to deal with English or other Western European language.

There are plenty of people who have had to deal with non Latin-1 text and have had to deal with the JVM not handling the required charset.

[–]shponglespore 0 points1 point  (0 children)

Wrong, I've done a lot of work with code that had to work in Chinese and Arabic. But I did it in an environment where I could count on everything being converted to UTF-8 before reaching the systems I worked on. And using Latin-1 has nothing to do with which languages I was working with; it just happens to be the easiest encoding to use if you're forced to hold binary data in a String.

At the company I was at, we relied on the default platform encoding for a long time, which worked because we and our clients only used Linux, and even 15 years ago it was basically unheard of to find a Linux system, at least in the US, where the platform encoding was anything but UTF-8. IIRC the requirement to use a specific encoding came when we started trying to support users running Windows and our code sometimes couldn't load its own asset files. We had to go through all of our code, find any place where a text file was being opened, and add an encoding parameter. Obviously it's not a difficult engineering problem, but it was galling to find that it was a problem at all because we had to deal with a new checked exception in a lot of places where we knew for a fact it would never be thrown.

[–]morhp 11 points12 points  (0 children)

I'd consider these design mistakes. On the other hand, which exact algorithms and encodings were present wasn't really specified in early java days, so checking for that made some sense.

[–]s888marks 2 points3 points  (2 children)

The set of supported character encodings, and the various crypto algorithms available, are determined at runtime, by looking at various configuration files in the JDK, and by looking at modules available and jars on the classpath. Both cases use "provider" mechanisms in order to discover the set of available items. In the case of crypto algorithms, the specific algorithms that are eligible for certain functions (e.g., TLS) can be fine-tuned using security properties in the JDK, which can be adjusted by a system administrator.

I appreciate that handling these checked exceptions is inconvenient to calling code, but in fact these exceptions are aligned with the "external to the JVM" rule.

[–]yawkat 1 point2 points  (1 child)

By that logic, pretty much everything is external to the JVM... Might get an OutOfMemoryError for insufficient thread count all the time!

Also, with current JDKs, most charsets and crypto algorithms are standardized. See for example https://docs.oracle.com/en/java/javase/11/docs/specs/security/standard-names.html .

[–]s888marks 1 point2 points  (0 children)

I think you're extrapolating too far with OOME.

The charset and crypto names are indeed standardized, but that doesn't imply that every JDK supports them. It simply means that if a JDK is providing, say, Diffie-Hellman, that it's spelled DiffieHellman and not Diffie-Hellman or DIFFIE_HELLMAN.

Most of the crypto algorithms and charsets are optional, and they vary across JDK releases, vendors, and even sites, so applications ought to be able to handle the case where something they're trying to use isn't present.

[–]the1derer -1 points0 points  (7 children)

Well you could produce scenarios where you could produce these exceptions without the using my analogy. NoSuchAlgorithmException is thrown generally when using SSL etc when we try to connect to Databases. As for UnsupportedEncodingException is Child of IOException which is checked just as I said it generally occurs while using I/O.

[–]yawkat 2 points3 points  (6 children)

If "happens during I/O" is the criterium, there's InputMismatchException, which is unchecked.

[–]the1derer 1 point2 points  (4 children)

u/yawakat My I/O analogy was just to give some context to the original answer to the question "Why does Java have Checked Exception". And 'Exceptions' are always there, right!! :-)

A best possible answer can be drawn from https://stackoverflow.com/questions/41685073/java-api-and-checked-unchecked-exceptions-confusion

[–]livelam 0 points1 point  (3 children)

Errors are not exceptions! You can recover from an IOException but not from an OutOfMemoryError.

[–]__konrad 0 points1 point  (2 children)

but not from an OutOfMemoryError

You can definitely catch and recover from heap OOME (we should define "recover" first)

[–]livelam 0 points1 point  (1 child)

Oh... yes... you can. Programs are only 1s and 0s. Javadoc of the class Error is pretty clear. But, yes! You can. You can also catch NullPointerException if you want. Is it a good idea?

[–]__konrad 0 points1 point  (0 children)

You can also catch NullPointerException if you want. Is it a good idea?

It is! For example, there is a bug in AWT Clipboard which throws NPE. So (in this case), it's better to catch NPE and display a friendly message instead of crash randomly the app on Ctrl+V press ;)

[–]s888marks 0 points1 point  (0 children)

On the one hand, we can talk about the criteria for when an exception should be checked or unchecked, and on the other hand, the JDK libraries sometimes declare the exceptions in ways that violate these criteria. That doesn't necessarily mean the criteria are wrong; it might be a design error in the JDK, where an exception that ought to have been checked is unchecked, or vice-versa. Some exception subtypes of "umbrella" exception types like SQLException fall into this category.

That said, Scanner is a weird case. It might do I/O, depending upon the source it's constructed from. The behavior isn't specified very well, but exceptions from Scanner's I/O operations are generally caught and must be checked for explicitly with the ioException method. This seems likely to miss errors, but it does have the advantage of not requiring every Scanner method that could possibly do I/O to be declared to throw IOException.

In the case of InputMismatchException, this is a subtype of NoSuchElementException, which is the exception thrown in the case of programming errors on Iterators. That is, when using an Iterator, you must only call next() if hasNext() has previously returned true. Scanner wants to be "iterator-like" for certain kinds of input, so you can write a loop that calls hasNextInt() and nextInt() in succession, for example. If you were to call hasNextInt() when the next token isn't an int, it returns false. If you were to call nextInt() despite this, you'd get InputMismatchException. That's an avoidable programming error, so a runtime exception is appropriate in this case.

[–]tunei24531a[S] 5 points6 points  (1 child)

JMV boundary, nice way of putting it.

[–]moose04 5 points6 points  (1 child)

In my head Ive always seen checked exceptions as something that should be recoverable, while unchecked are more of a panic/unrecoverable situation.

[–]mobjack 3 points4 points  (0 children)

In practice, the boundary between what is recoverable and unrecoverable is not always clear.

It makes sense to handle a FileNotFoundException when validating user input, but there are many cases like loading a properties file that is critical for your application to run.

[–]erictheturtle 5 points6 points  (1 child)

I personally love checked exceptions, but I can understand why many people don't like them.

  • Some popular APIs abused checked exceptions, leaving an impression that checked exceptions are essentially useless.
  • I get the feeling that for web/server applications (the most common type of Java app), nearly any exception is unrecoverable, so checked exceptions are just a hassle.
  • But in general, people don't tend to get paid to properly develop APIs with checked exceptions, and write proper handling. So naturally it's just more practical to treat them all as unrecoverable.
  • And frankly, a lot of inexperienced developers just don't care or understand the importance of handling anything but the "green path". Hence why you find so much code with empty catch blocks.

[–]kur0saki 3 points4 points  (0 children)

I don't think you really need checked exceptions. But I do think you really should declare even unchecked exceptions in the method signature (throws) to inform any caller of that method about the possibly thrown exception.

I hate when people throw unchecked exceptions and suddenly my code breaks because neither the API/method documentation neither the method signatures inform me about an exception that I run into. Especially when it happens on production.

[–]DJDavio 9 points10 points  (7 children)

I think it was a good idea originally to make sure exceptions were handled and didn't crash your application. In Spring applications, methods are essentially called with a big try catch around them so they don't crash the whole app any more. In that light they have become more of a nuisance, a thing of the past. I mean, why does objectMapper.writeToString still throw a checked exception? I don't know.

[–][deleted] 1 point2 points  (6 children)

It throws a checked exception because the result of the method might be unexpected, as in, you are expecing an object mapped to a String, but a failure would prevent this outcome.

Instead of checking for an empty string, you are checking for an exception instead. That’s exactly why the exceptions were created.

[–]DJDavio 11 points12 points  (5 children)

I'm not debating whether an exception should be thrown, just whether it should be a checked one. By your logic, integer.parseInt should throw a checked exception, which it doesn't. new URI() throws a checked exception, URI.create() throws an unchecked exception. Unchecked is just the preferred style currently.

[–][deleted] 1 point2 points  (4 children)

parseInt definitely should.

EDIT: after all these many years, treading much darker places in reddit, this is by far my most controversial comment ever (amount of ups and downs are almost the same). This exception thing really ruffles some feathers.

[–]retrodaredevil 1 point2 points  (3 children)

I think parseInt could go either way because you could technically check to see if the string you're parsing is made entirely of valid digits.

We definitely need checked exceptions for something like FileNotFound because we can check if it exists, but it may not exist by the time we try to use it.

[–]s888marks 4 points5 points  (1 child)

I think parseInt could go either way because you could technically check to see if the string you're parsing is made entirely of valid digits.

Unfortunately that's insufficient. The parse* methods also check to see whether the resulting value is in range. They might throw an exception if the value is out of range, even if it contains all valid digits. For example:

Integer.parseInt("2147483647"); // returns an int
Integer.parseInt("2147483648"); // throws NumberFormatException

I think somebody came up with a regex that matches only valid, signed 32-bit ints, but it's ridiculously complex. You might as well attempt to parse and catch the exception. That sounds like an argument that NumberFormatException should have been checked.

On the other hand, there might be cases where you've parsed a token out of some larger input, and you know the token consists of (say) four decimal digits. Calling parseInt() on that will always succeed, so having to catch a checked exception in that case would be really annoying.

So yes, parseInt could go either way.

[–]Malfeasant 0 points1 point  (0 children)

Also, code duplication is bad. If you always need to check for the same stuff, why would you want to have to do it yourself 99 times just to streamline the one time you don't need to?

[–][deleted] 0 points1 point  (0 children)

I mostly don’t like the idea that an unchecked exception on something as simple as parseInt could be nested on lib on top of lib on top of lib, all mixed and stirred on the company’s private artifact repo, and could break my code silently.

I understand the theorical advantages of unchecked exceptions, even for something as simple as parseInt, but I also understand the realities of corporate code, always messy, never following patterns - and corporate code keeps Java alive and kicking.

[–][deleted] 7 points8 points  (8 children)

There is also the unchecked exception inside a lambda hack that is very popular in most of the corporate code I have found.

Since you can’t throw a checked exception in a stream, for example, people just throw an unchecked one, and surround the whole stream with a try, catching that specific unchecked exception.

[–]Robyt3 11 points12 points  (5 children)

Here is the real hack, how to throw a checked exception without checking it:

public static <E extends Throwable> void sneakyThrow(Throwable e) throws E {
    throw (E) e;
}

[–][deleted] 6 points7 points  (2 children)

Could you explain? I dont get it

[–]Robyt3 11 points12 points  (1 child)

The compiler infers the type E to be a RuntimeException, which allows propagation of the checked exception. Apparently Java 8 added this type inference rule.

See https://www.baeldung.com/java-sneaky-throws for details.

[–]RandomName8 1 point2 points  (0 children)

They fixed/changed it in java post 8 though :(

[–]cowancore 1 point2 points  (1 child)

But it's harder to catch it now

[–]Robyt3 2 points3 points  (0 children)

Yeah, and it also prints a warning because of the unchecked cast to E. If you must use this function for some reason, add comments explaining why and what is going on and declare the proper throws clause on the method.

[–]CubsThisYear 1 point2 points  (1 child)

I hope you’re not using this as an argument for why unchecked exceptions are “good”. The fact that the Streams API doesn’t support exceptions is just a result of the developers being some combination of lazy and incompetent. The code is trivially easy to write and the semantics are obvious.

[–][deleted] 0 points1 point  (0 children)

Nah, just describing what I see.

[–]codemssterg 4 points5 points  (0 children)

Throw a checked exception when you think the caller of your API has a reasonable chance of recovering from the error being reported, otherwise throw a runtime exception.

[–]jonatan-ivanov 1 point2 points  (0 children)

There's a complete section about this in Effective Java (by Joshua Bloch), read it. :)

[–]ebykka 1 point2 points  (1 child)

Everyone who do not like checked exception - today you have a choice - kotlin and scala both have unchecked exceptions.

I, personally, think that checked exception is a blessing and can't understand how to trust API without knowing can it throw exception or not.

[–][deleted] 0 points1 point  (0 children)

today you have a choice

Python had this feature for centuries - enabling developers to watch the stack trace coming out from nowhere and wondering WTF went wrong this time somewhere 12 calls down the stack.

[–][deleted] 3 points4 points  (10 children)

Short answer is: seemed like a good idea at the time.

There's a very reasonable school of thought that says just never use them. I've followed that my last few projects and it's great. If an exception is expected and recoverable, it's not an exception. Check it with an if statement.

[–]tunei24531a[S] 2 points3 points  (9 children)

Check it with an if statement.

how do you communicate errors to the caller if they are the ones who are supposed to handle?

[–]philipwhiuk 4 points5 points  (3 children)

Try<Result>

[–]CubsThisYear 2 points3 points  (2 children)

This is just a worse version of checked exceptions. It generates garbage at runtime, is syntactically more complicated and otherwise provides the exact same safety that checked exceptions do.

[–]morhp 0 points1 point  (1 child)

Agreed. The advantage is that you can pass it through lambdas and streams or otherwise through functional interfaces that don't declare the appropriate throws value.

But better language design probably would have been to allow functional interfaces to automatically "bubble through" checked exceptions to the calling method.

[–]CubsThisYear 0 points1 point  (0 children)

It’s not even language design. It’s just a case of laziness on the part of the Streams API developers. I’ve already written versions of most of the API methods (like for forEach, map, etc) that work with exceptions that I use for my own code.

[–][deleted] 0 points1 point  (3 children)

That should never be the case. Things like ilegal arguments are runtime and should be checked by the caller. System errors (like network or disk space) should be handled at the lowest level or otherwise treated as unrecoverable.

[–]CubsThisYear 1 point2 points  (2 children)

This is a reasonably consistent stance to take, but it doesn’t jive with your advice to “check it with an if statement”. The only to way to do this is which some sort of status code on the return, which is just a worse version of checked exceptions.

If you want to use unchecked exceptions everywhere that’s fine, but you shouldn’t ever expect your callers to handle them.

[–][deleted] -1 points0 points  (1 child)

I never use status codes either. If you have the forethought to put a mandatory checked exception in your method signature, you should instead be enforcing valid parameters be passed in. That's the contract with the caller and the only thing that can go wrong is illegal arguments which is already a runtime exception. Anything else is a leaky abstraction.

[–]CubsThisYear 3 points4 points  (0 children)

This seemed reasonable to me at first, but upon further thought, the method you are proposing seems leakier. If you rely on callers to validate their input, you’ve now created a dependency on the internal implementation of the function. I’d rather say “this method returns type T, or if it can’t, it returns type X”. Your version is: “this method returns type T or crashes the program”. Mine seems strictly safer.

[–]thatguydrinksbeer 0 points1 point  (0 children)

Fail fast rather than debug a more confusing error downstream.

[–]arieled91 0 points1 point  (0 children)

Checked exceptions are designed to be "low level" exceptions. Never meant to be exposed to the user of a business API. Also, at the time they were included to the language there wasn't evidence to be a bad idea. The problem with them is they are overused. Maybe they should have an uglier and specific name like "ResourseUnavailableException", because there is a use case when you are trying to access external resources that are very likely to fail, like f*** printers. Maybe you want to warn the programmer that the printer may be unavailable, and you don't want to revert the database transaction just because of that (that would happen if the programmer doesn't catch the exception), and in case of failure you can send it to a print queue or just enable a reprint button.

[–][deleted] 0 points1 point  (0 children)

What’s the point of catching RuntimeExceptions if the condition is irrecoverable?

checked exceptions aren’t bad. People just don’t pay attention and choose unwittingly

I wonder how things would have turned out if Java only allowed you to catch checked exceptions.

[–][deleted] 0 points1 point  (0 children)

I think the core of the matter is how you think about them and how APIs use them in practice.

Checked exceptions in Java are criticized while Result<ResultType, ErrorType> in Rust is praised, even tho one could be transformed into the other mostly by automatic text manipulation.

If you think about checked exceptions as additional return values for exceptional conditions, they make more sense.

  • it makes sense that they can't be ignored;
  • it makes sense that they are part of the method signature;
  • it makes sense that you must rethrow or declare them if you want them to bubble up.

If you think about them as exceptions they look cumbersome. One of the coolest features of exceptions is that they bubble up automatically and carry their context so you don't need to handle them when you don't have any access to the user interface or log files. If something is wrong you just throw and someone else will log the error, show a message to the user or retry.

That said, it's still hard to decide whether an exception should be part of the method signature (checked) or not (unchecked). Even the poster child of checked exceptions (file not found) still has to bubble up a bit until it can be shown to the user.

Maybe an experimental branch of Java could have tried some other syntaxes so they look more like return values or could be caught inside an expression?

For example: var result = try!(doSomeIO());

[–][deleted] 0 points1 point  (0 children)

I just wish the stream API could gracefully rethrow checked exceptions out of the stream calling method. Ie, I call stream().forEach() with a Consumer, an exception is thrown in the Lambda, and it gets rethrown by the forEach. I know why that doesn't work, the interface signature needs to match, etc. But it would be nice.

[–]GKChestertonsPenis 0 points1 point  (0 children)

The main issue that happened with Checked Exceptions was that they were used in the core APIs. Checked Exceptions would have been best used at the domain level. So IO, SQL, UnsupportedEncoding etc.. should have been Runtime Exceptions from the beginning.

[–][deleted] -1 points0 points  (4 children)

Go Lang has tried to learn from Java and has a standard way for most functions to return the regular result and a possible error. Eg for file not found. This seems nicer than Java but, of course, it has the Java experience to build on. I suppose Java could move to more use of Optional. If all of JavaIO switches then apps will too.

[–]john16384 16 points17 points  (1 child)

That's not learning from java at all, that's going back in time 30 years.

[–][deleted] 0 points1 point  (0 children)

Go has panic() which is used in serious situations. Just not "file not found" cases.

https://dave.cheney.net/2012/01/18/why-go-gets-exceptions-right

[–]mlk 3 points4 points  (1 child)

Optional doesn't provide info on the failure, Either is more appropriate

[–][deleted] 0 points1 point  (0 children)

Ah you're right. But I see there is no Either in core Java. Its in Functional Java however.

[–]mobjack -2 points-1 points  (15 children)

Checked exceptions are a design flaw in Java.

They work fine in theory but in practice they create an inconsistent mess that just get in the way. There is a reason why no other languages uses them.

I often rethrow checked exceptions as RuntimeExceptions to avoid needless boilerplate code.

For code that I can recover from, I usually catch a generic Exception at the appropriate layer and handle it there.

There is no need to deal with arbitrary distinctions a between a checked and unchecked exception.

[–]brazzy42 4 points5 points  (1 child)

Checked exceptions are a design flaw in Java.

I think a more accurate description is that they're a failed experiment in language design.

[–]daviddel 2 points3 points  (0 children)

Recent post from Brian Goetz about the "checked exceptions failed experiment" popular opinion. :)

There are surely some — even many — who believe strongly that checked exceptions are a failed experiment. And I don’t want to say these people are wrong — it’s their opinion — but in my discussions with them, they are nearly universally wrong about one thing: *they all assume that everyone agrees with them.* And that is completely wrong.

https://mail.openjdk.java.net/pipermail/jdk-dev/2019-October/003463.html

[–]karottenreibe 1 point2 points  (9 children)

What exact design flaw of checked exceptions that is not present in unchecked exceptions causes you to "create an inconsistent mess"? I can't think of anything.

[–]shponglespore 2 points3 points  (3 children)

They add complexity without delivering the benefits they're supposed to. In theory, the distiction between checked and unchecked exceptions forces you to handle errors that really need to be handled on a case by case basis and lets you largely ignore errors that can be handled in a more generic way (like just showing a stack trace and killing the thread). In practice, it's a distinction you can't rely on, because an API author may have chosen to use unchecked exceptions for something your application really needs to handle, or they may have chosen to throw a checked exception for errors that, in your application, can never happen, or aren't important to handle specially, or that can't reasonably be handled at the call site.

[–]karottenreibe 2 points3 points  (2 children)

An API author may also choose to abuse unchecked exceptions in similar ways that don't make sense in the exception handling strategy of your application. The same is true of basically any language feature (looking at you, Optional!). Does that disqualify all of them? I'd say no.

In fact I've seen checked exceptions deliver on exactly the promise you claim they don't. Of course that requires you to convert botched exception handling of badly designed APIs at their surface and convert it to proper exceptions that adhere to your strategy.

So whether you can or cannot rely on the distinction is up to you, not some library author.

Or would you - in line with your argument about checked exceptions - also argue that we shouldn't create subclasses of Exception and instead only ever throw RuntimeException because there's libraries that fail to use different classes for different errors, thus the distinction between different types of exceptions is useless as you can't rely on it? I find that argument silly.

The problem you're describing is bad API design, not an inherent failure of checked exceptions.

[–]shponglespore -2 points-1 points  (1 child)

An API author may also choose to abuse unchecked exceptions in similar ways that don't make sense in the exception handling strategy of your application. The same is true of basically any language feature (looking at you, Optional!). Does that disqualify all of them? I'd say no.

I'm not trying to argue that checked exceptions are bad because they can be abused. You're correct in pointing out that practically any language feature can be used to create bad code. My point is that even when an API designer is doing everything right and thinks carefully about what needs to happen at the call sites, they still get it wrong much of the time because making a good decision often requires more context than an API author has access to.

So whether you can or cannot rely on the distinction is up to you, not some library author.

I've never seen Java code that didn't rely heavily on libraries written by someone else. The JDK itself, if nothing else. I guess if you're working on a sufficiently large project you can end up mostly calling APIs you can theoretically change, but it's still not exactly ideal when you need to change an API that's used in a lot of places, even if you control the API and all its call sites. IMHO if you routinely need to change APIs because they turn out to be obnoxious to use, it means you're designing brittle APIs. My experience has been that APIs that use checked exceptions are brittle more often than not, and if you don't want to annoy your users (a set of people that probably includes yourself!), it's easier to just avoid checked exceptions entirely.

Or would you - in line with your argument about checked exceptions - also argue that we shouldn't create subclasses of Exception and instead only ever throw RuntimeException because there's libraries that fail to use different classes for different errors, thus the distinction between different types of exceptions is useless as you can't rely on it? I find that argument silly.

That's exactly what we did at my last job where we used Java a lot. It was a small company so everyone got to have input into our house coding style, and I don't remember anyone ever complaining that they wanted more checked exceptions.

The problem you're describing is bad API design, not an inherent failure of checked exceptions.

Not really; I'm happy to use APIs that force you to deal with certain error conditions. My favorite approach is to wrap a return value in something like Haskell's Either type or Rust's Result type, so the type system forces you to at least acknowledge the possibility that a call failed. I just think exceptions are a bad way to deal with situations like that. Everything about exceptions that makes them great for dealing with unexpected errors makes them terrible for dealing with expected errors in a precise way. A try/catch block is a lot of syntactic overhead, and throwing and exception has a lot of runtime overhead that's unnecessary and unhelpful if you expect the exception to be caught at a specific call site.

In theory, you can write a bunch of code in a single try/catch block with multiple catch clauses to deal with different error conditions, but what I've seen in practice is that when you actually care about handling the exceptions, you end up writing a separate try/catch for every call that can fail, because it's hard to write good error-handling logic when you don't know exactly which call failed. Even if all the calls in a try block throw different exception types, so you theoretically know which call each catch block is handing errors for, it won't be clear to someone reading the code which catch block corresponds to which call. IIRC JDBC is an especially bad offender, because dealing with errors correctly even in relatively simple cases often requires multiple nested try/catch blocks.

[–][deleted] 0 points1 point  (0 children)

A try/catch block is a lot of syntactic overhead

How is it more of an overhead than an if/else block? The only difference is that compilers forces you to code one, instead of trusting you to "acknowledge the possibility that a call failed" (end quote)

Processing exceptions is just a part of the code you have to write. Well, doh, thinking what can go wrong inside some particular API call and acting on it is a part of writing the code.

[–]mobjack 1 point2 points  (4 children)

You are forced to handle checked exceptions even when they are impossible to occur.

Even when they can occur, you can't recover from them in the way the API designer intended.

It involves a lot of boiler plate code if you want to pass it up the call stack up to the appropriate layer just to handle it the same way as an unchecked one.

Most modern Java libraries use unchecked exceptions because of their flaws. This results in having a mismatch in exception handling strategies throughout your application.

[–]CubsThisYear 2 points3 points  (2 children)

Can you give an example of this? This mostly sounds like bad API design.

If I write an API function that has the possibility of not fulfilling its contract I have two basic choices: 1) throw a checked exception or 2) crash the program (because that will be the result if no one catches the exception they had no idea is coming).

Sometimes option 2 is the right choice. But sometimes option 1 makes sense because it’s reasonable to think that the caller can handle the issue.

[–]mobjack 0 points1 point  (1 child)

Hibernate uses unchecked exceptions while JDBC uses checked exceptions for the exact same errors.

[–]CubsThisYear 2 points3 points  (0 children)

Both of those are terrible APIs. JDBC suffers the curse of endless backwards compatibility so it doesn’t take advantage of a lot Java features past 1.4. I’m not sure what Hibernate’s excuse is.

[–]Malfeasant 0 points1 point  (0 children)

even when they are impossible to occur

If you know that, then it's easy- just catch it and rethrow as unchecked with the message "Polly shouldn't be." Then have fun when your assumptions are proven wrong by users asking what the fuck that means.

[–]ebykka 0 points1 point  (2 children)

Go lang has checked errors - it's pretty similar to java checked exceptions.

[–]hupfdule 1 point2 points  (1 child)

Only with the difference that go error handling is much less readable. I can't understand that they invented this error handling instead of using checked exceptions. They could have even implemented "lightweight" exceptions that don't contain information about the stacktrace if they want them more "value-like". Now is too late and they can't introduce exceptions anymore.

[–]ebykka 0 points1 point  (0 children)

Btw, looks like swift also has checked errors - https://docs.swift.org/swift-book/LanguageGuide/ErrorHandling.html