4 ms·
I’ve heard that claim many times before. I’m a daily Java developer, and can provide a little perspective on what Java is like. Though I don’t know the exact sp
by MBCook 2y ago
I’ve heard that claim many times before. I’m a daily Java developer, and can provide a little perspective on what Java is like. Though I don’t know the exact specifics of the claim.
In Java a given method either doesn’t throw any exceptions, or only throws the named ones and their children. So nothing, or maybe just InvailArgumentException (and its descendants).
However there’s a lot of different kinds of exceptions. So very quickly you tend to end up with one of two things. Functions have a big list of possible exceptions (an A, or B, or C, or F, or G) or the far more common in company code: throws Exception. Don’t bother to list stuff, just say “I throw stuff”.
Except, that’s all a lie. Because every function implicitly can throw RuntimeException and its children. Things like OutOfMemory or StackOverflow.
So basically all code everywhere realistically has to be aware of exceptions. A lot of people get lazy and don’t bother to list the actual kinds of exceptions they throw or pick one that’s a runtime exception so they don’t have to add the ‘throws’ to the signature to every single method.
And how do you solve calling a method with a checked exception? Well the right thing to do is use a try block, but so often people just add it to their own signature and pass the buck.
So a lot of the benefits just sort of get lost.
Swift now has three options: doesn’t throw, throws something, throws of type X. You can’t have multiple types. And you can’t pretend the exception doesn’t exist and just let it bubble up, you have to put a ‘try’ on the statement line to do that.
As someone who works in both languages Swift’s approach does seem like an improvement. I’m not sure how big.
But just in Swift, being able to explicitly state the one type of the exception that may be thrown seems like a very nice add for type safety and reducing boilerplate checks.
- koito17 2y agoMinor nitpick: OutOfMemoryError and StackOverflowError are subclasses of Error, not Exception. It is an anti-pattern to catch any Error subclasses since these are usually JVM-internal exceptions. Programs are firmly in undefined behavior territory when they e.g. catch an OutOfMemoryError and do nothing with it. This is why catching Throwable is considered a bad idea compared to Exception. In the majority of cases, there is no reasonable recovery from a thrown Error, unlike a thrown Exception. With that said, it is true RuntimeException and its subclasses are unchecked exceptions. Thankfully, those exceptions tend to be things like IllegalArgumentException, NullPointerException, etc., which are still pretty serious but not as bad as the two examples given.
- MBCook 2y agoAh that’s right. I’m used to RuntimeException since that’s what people subclass to get out of the checked exception mess. The exact hierarchy around rare things like OutOfMemory just isn’t something that ever comes up in day to day work so it’s easy to forget. Thanks for the correction.
- dwaite 2y agoThe ThreadDeath Error in particular is interesting because it is catchable but not "handle-able" - if you do not rethrow ThreadDeath in your exception handler, the JVM will do so for you. Despite Thread.stop() being deprecated, it is still used in things like application servers to deal with user code which is not completing in a specified timeframe.
- dwaite 2y ago> Except, that’s all a lie. Because every function implicitly can throw RuntimeException and its children. Things like OutOfMemory or StackOverflow. > So basically all code everywhere realistically has to be aware of exceptions. A lot of people get lazy and don’t bother to list the actual kinds of exceptions they throw or pick one that’s a runtime exception so they don’t have to add the ‘throws’ to the signature to every single method. Not just that, there are features like lambdas where the core language features were not designed with exceptions in mind. There are third party libraries which don't have allowances for proper failure cases. So you will have code adapted to wrap their exceptions into runtime exceptions for interoperability. Of course, there's no guarantee the code which did not provide an interface which allowed for exceptions will not have unexpected side-effects when runtime exceptions or errors are being thrown. > However there’s a lot of different kinds of exceptions. So very quickly you tend to end up with one of two things. Functions have a big list of possible exceptions (an A, or B, or C, or F, or G) or the far more common in company code: throws Exception. Don’t bother to list stuff, just say “I throw stuff”. Ideally, someone creating an API thinks about how the others utilizing code will accomplish recovery or graceful failures, and craft an API to account for this. Unfortunately, the errors are often handed up, which really only accounts for broad failure behavior and corresponding cleanup (such as failing the corresponding HTTP request with a generic 500 error). The heavy use of adapter patterns in Java means that the errors being handed up may be specific to the configuration, such as failures coming from the network layer of a database backend. It is crucial for the adapter interfaces to think about failure scenarios and recovery, but it unfortunately doesn't happen often very often. So in most cases, Java should just throw Exception, because the only way a developer can really attempt recovery is to break encapsulation and operate based on knowledge of the inner workings of the object they were handed.