by flying_sheep on 9/11/20, 8:57 AM with 317 comments
by alasdair_ on 9/11/20, 4:14 PM
I notice this especially with less experienced developers and remote calls - a lot of JS code I’ve reviewed in the past assumes the remote call will always work, yet Java code from the same developer will almost always correctly handle the situation, simply because the exception is explicitly required to be handled.
by hrgiger on 9/11/20, 3:32 PM
by gordaco on 9/11/20, 2:49 PM
Nevertheless, I still believe that Java's biggest mistake is not checked exceptions, but the stupid distinction between primitive types and Objects (caused because all the cruft present in the latter would have make basic operations prohibitive in terms of performance, at least for early Java versions) and all the associated boxing. I have seen some extreme cases of performance degradation because of that (fortunately a refactor to use arrays solved the problem, but this is not always possible).
by aazaa on 9/11/20, 8:10 PM
Here's what Oracle has to say:
> 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.
https://docs.oracle.com/javase/tutorial/essential/exceptions...
- checked exception for recoverable errors
- unchecked exception for non-recoverable errors
So the argument that most errors can't be recovered from is _not_ a reason to abandon checked exceptions. It's a reason to reserve checked exceptions for those cases in which recovery is likely.
The main argument in this article appears to be based on a misunderstanding.
by specialist on 9/11/20, 3:04 PM
I've never understood the angst. All the arguments reduce down to mitigating terrible abstractions.
The Correct Answer is better APIs. Mostly, that means don't pretend the network fallacies don't exist. Embrace them. Which generally means work closer to the metal.
I'll say it another way. The problem is EJB, ORMs, Spring, etc. The obfuscation layers.
Someone smarter than me will have to rebut the functional programming points. I'd just use a proper FP language. Multiparadigm programming is a strong second on the list of stuff you shouldn't do. (Metaprogramming is first.)
by Imnimo on 9/11/20, 3:44 PM
But then there are other types of checked exceptions that are almost certainly unrecoverable because they happen way down in some other third party code. And then you get the endless chain of "throws" all the way back up the code base.
by chriswarbo on 9/11/20, 3:11 PM
My preferred style of error-handling is Option/Either, since I can implement the 'happy path' in small, pure pieces; plug them together with 'flatMap', etc.; then do error handling at the top with a 'fold' or 'match'.
Exceptions break this approach; but it's easy to wrap problematic calls in 'Try' (where 'Try[T]' is equivalent to 'Either[Throwable, T]').
The problem is that Scala doesn't tell me when this is needed; it has to be gleaned from the documentation, reading the library source (if available), etc.
I get that a RuntimeException could happen at any point; but to me the benefit of checked exceptions isn't to say "here's what you need to recover from", it's to say "these are very real possibilities you need to be aware of". In other words checked exceptions have the spirit of 'Either[Err, T]', but lack the polymorphism needed to make useful, generic plumbing. The article actually points this out, complaining that checked exceptions have to be handled/declared through 'all intervening code'; the same can actually be said of 'Option', or 'Either', or 'Try', etc., but the difference is that their 'intervening code' is usually calculated by the higher-order functions provided by Functor, Applicative, Monad, Traverse, etc.
It's similar to many developer's first experience of Option/Maybe: manually unwrapping them, processing the contents, wrapping up the result, then doing the same for the next step, and so on. It takes a while to grok that we can just map/flatMap each of our steps on to the last (or use 'for/yield', do-notation, etc. if available). It would be nice to have a similar degree of polymorphism for checked exceptions. Until then, I'd still rather have them checked (so I can convert them to a 'Try'), rather than getting no assistance from the compiler at all!
by captainmuon on 9/11/20, 7:29 PM
It would be great if they worked the other way around. Instead of forcing the caller to catch an exception, they would guarantee that no exception leaves a certain block.
So you would have a function
void MyFunc() onlythrows IOException {
first();
second();
}
And the compiler would statically guarantee that no other exception can leak out of it - because first and second have been marked `onlythrows IOException` or are "pure" and cannot throw at all.For sure you'd need an escape hatch, like Rust's "unsafe". And it would not be very useful around legacy libraries. But it would be tremendously useful if you could drop a block like
neverthrows NullPointerError { ... }
in your code and be sure that everything inside is null safe! I asked about this a few years ago on StackExchange [1] but so far I never heard about it anywhere else.[1] https://softwareengineering.stackexchange.com/questions/3497...
by iso8859-1 on 9/11/20, 8:42 PM
catch (MyCheckedException e) {
e.printStackTrace();
}
This causes unreliable programs, since the programmer will initially only think about the successful path. Eventually, the exception will get thrown, and things will break in weird ways. They may not notice it quickly, because the stack trace will be buried in logs. Alternatively, if the IDE default has been throw new RuntimeException(e)
or something similar, which would crash the program, the programmer would have noticed it more easily. Of course, the program would still be broken, but better crash hard and violently than subtly and confusingly.by grey-area on 9/11/20, 3:40 PM
I once attended a Java user group meeting where James Gosling (Java's inventor) was the featured speaker. During the memorable Q&A session, someone asked him: "If you could do Java over again, what would you change?" "I'd leave out classes," he replied.
https://www.infoworld.com/article/2073649/why-extends-is-evi...
by pmcollins on 9/11/20, 3:18 PM
So now we have consultingware like Spring where if something isn't working, it could because you missed an annotation somewhere, or put the right annotation in the wrong place. Which annotation? Where? Maybe you'll find out a week from now that you made a mistake, when a customer finds a bug in production.
This took all of the compile-time checking goodness that you got from Java and threw it in the garbage. Now you either have to call an expensive consultancy, read books/manuals about your gigantic framework (fun!), go on forums, etc. You can't just use your coding skills.
I still often use Java for my side projects because I love it without runtime annotations, but thank god for the rise of Golang. I'd rather deliver pizza than go back to the misery that is annotation-driven development in Java.
by crehn on 9/11/20, 4:06 PM
by hodgesrm on 9/11/20, 3:03 PM
It conflates thread management with exception handling in a way that's difficult to understand and implement correctly. The relationship between InterruptedException and the Thread.isInterrupted() method is a particular pain point for coders.
by josephcsible on 9/11/20, 5:04 PM
1. Built-in exceptions that are checked but should be unchecked, IOException being the main offender (I don't mean things like FileNotFoundException; I mean the kind you can get if the OS returns -EIO)
2. Lack of exception polymorphism, preventing you from doing things like l.stream().filter(SomeClass::somePredicateThatMayThrow), even if the function that you're doing it from can throw the same exception that the predicate can
I think checked exceptions would be great and nobody would hate them if those two problems were fixed.
by kasperni on 9/11/20, 2:53 PM
by pjmlp on 9/11/20, 3:00 PM
Also checked exception haters always overlook the fact that CLU, Modula-3 and C++ did it first.
by mumblemumble on 9/11/20, 5:07 PM
I quickly found that checked exceptions just do not play nice with any sort of functional-style programming, like the article describes. But the problem goes so much deeper than that. Checked exceptions are also, as far as I can tell, incompatible with an object-oriented programming style. More or less for the same reason that they interact poorly with FP. The fundamental problem is that checked exceptions don't really play nice with polymorphism or higher-order programming of any type.
Which takes us to the crux of how I understand them now: Checked exceptions may not go well with FP and OOP, but they make all the sense in the world if you're doing procedural programming. There, you're not trying to create deeply nested abstractions, and you're not messing around (much) with polymorphism tricks. The code's very lexically organized, with little in the way of dependency injection or higher-order programming. When you're programming procedurally, it's fine to be exposed to the implementation details of stuff further down on the call graph, because you're the one who put it there in the first place.
And that, in turn, means that checked exceptions are not really a mistake. They're just a piece of evolutionary history. Because, early on, Java wasn't really an object-oriented language. It was a deeply procedural language with some object-oriented features. It arguably still is, it's just that there's been a big cultural shift toward trying to take a more object-oriented approach since Java 5 came along and made it more practical to do so.
by crehn on 9/11/20, 4:15 PM
String s = null;
switch (s) {
default:
System.out.println("Hey");
}
Hint: it will throw NullPointerException.by jarym on 9/11/20, 3:17 PM
For me type erasure is a bigger issue. I get that it was done for backwards compatibility but the drawbacks imposed by that decision seem to only grow as more time passes and more new compromises have to be made.
by imglorp on 9/11/20, 5:13 PM
We applied ROP with great success at a fintech where we wanted to clean up a block of business logic with many failure paths. Instead of a forest of nested conditionals or try/catch mess, there was a very simple happy path with clear handling for all the errors.
Here's a good start. Ignore the language details, the concept is universal. https://fsharpforfunandprofit.com/rop/
by ben7799 on 9/11/20, 3:05 PM
Even with all the functional stuff they almost never seem to really create a major issue.
My biggest issue with Java is just the way they've caved and constantly added new stuff that is always grafted on so it's never quite as good as a language that focuses on that programming paradigm from the start.
But none of the problems in the language compare to the scale & scope of the problems caused by Java's default developer & architect culture. The culture is terrible... everything gets overcomplicated, overabstracted, etc. and you've got charismatic charlatans convincing wide swaths of developers to misuse and abuse language features in ways that have made a lot of people hate the language and have produced a lot of buggy and hyper inefficient code.
Java itself doesn't have to be bloated, slow, buggy, and a massive memory hog. But the java developer community has continually made decisions to structure their java software in a way that makes that the default condition of Java systems. The way the Java language constantly gets new giant features grafted on plays into this.. everyone jumps on the latest language addition and misuses it for a few years before they come to understand it. By the time it's understood there's something new to move onto and abuse.
Java became everything about C++ it was originally supposed to simplify.
by adrianmonk on 9/11/20, 6:53 PM
A big part of the reason people hate checked exceptions is that actually doing #1 correctly is really damn hard. It's a whole separate dimension of complexity that your design needs to tackle.
A compiler that checks exceptions forces you to do it. Abruptly, if you're new to the language. It flips the floodlights on at full brightness and makes you see the full scope of the problem. It's tempting to shoot the messenger.
by Reason077 on 9/11/20, 4:08 PM
by devit on 9/11/20, 4:24 PM
However Rust, unlike Java, has a great macro system and can thus easily generate higher level exceptions wrapping the lower level ones.
by nayuki on 9/11/20, 4:53 PM
by samfisher83 on 9/11/20, 4:27 PM
by bcrosby95 on 9/11/20, 2:35 PM
by spion on 9/12/20, 1:21 AM
Java's checked exceptions got the worst possible combination of error tracking. Its optional, so you don't even get to see if a function throws (kind of like the billion dollar null mistake) and its based on classes, which means a lot of irrelevant names leaking throughout the codebase.
Like with nulls, the main value is being able to claim that a function doesn't ever throw, at all. Apple's Swift got this just right.
A simpler system of "throws / doesn't throw" and with optionally polymorphic variants / unions to complement it would go much further.
by flowerlad on 9/11/20, 10:05 PM
See long discussion here: https://forum.dlang.org/thread/hxhjcchsulqejwxywfbn@forum.dl...
by noisy_boy on 9/11/20, 4:57 PM
by CornCobs on 9/12/20, 6:17 AM
by mcculley on 9/12/20, 12:55 PM
A long time ago, I developed quite a lot of code in Ada83. Our team found having to use documentation to express what exceptions might be thrown led to many errors. I was pleased when Java came around that this was expressed directly in the function declaration.
But then it became clear that it lead to a lot of boilerplate.
I would like the throws keyword to just be an indication of what might be thrown and not require that I catch it.
by jayd16 on 9/11/20, 7:39 PM
You can use catch block style with typed exception handling or simple boolean checks depending on the situation and what you prefer.
by tomohawk on 9/11/20, 11:40 PM
Some checked exceptions, such as InterruptedException, should really be something else. I've very rarely seen this exception handled properly by anyone. Often, a general catch Exception will also catch this, and while the code will work just fine in most circumstances, random threads will not go away when they should.
It's a mess.
by bitwize on 9/11/20, 11:09 PM
by mcguire on 9/11/20, 7:54 PM
This isn't to say that checked exceptions are always used well. (What exactly am I supposed to do about an exception from .close()?)
by whitenoice on 9/11/20, 6:35 PM
by sreque on 9/11/20, 9:22 PM
Checked exceptions have all the disadvantages of monads:
* Checked exceptions don't compose with each other. You have to create manual witnesses of composition (methods throwing multiple exception types, or wrapping exceptions into new exceptions like a monad transformer)
* Checked exceptions infect the type system, having to be copied everywhere.
However, they have none of the advantages of monads, and more disadvantages besides. Java the language does not provide any facilities for abstracting over checked exceptions, and they interact terribly with any design involving higher order functions or any other higher-level abstraction.
It's time for the java community to admit they got this one wrong and move on.
by desertlounger on 9/11/20, 9:31 PM
by ncmncm on 9/11/20, 2:58 PM
by franzwong on 9/11/20, 3:36 PM
by storedbox on 9/11/20, 8:31 PM
by kanzenryu2 on 9/11/20, 8:31 PM
by tealpod on 9/11/20, 3:28 PM