Why Kotlin? An Introduction. (Part 3)

Error Handling: Something went wrong...

Why Kotlin? An Introduction. (Part 3)

Error Handling: Something went wrong...

Lets follow up the discussion about nullability and talk about error handling in Kotlin. Syntactically, the differences from Java are pretty nominal, except for one pretty major key difference that I'll get to in just a second.

First some code to demonstrate how similar Kotlin Exceptions are to Java's Exception handling:

fun doSomething(data : String) {
        if(data.contains("test")) {
            throw IllegalStateException("Test!")
        }
}

fun main() {
        try {
            doSomething("testData")
        } catch (e : Exception) {
            // This will always get called because an exception is thrown if a String containing "test" is passed to doSomething(...)
            Log.d("MyTag", "Exception thrown from doSomething(...)")
        } finally {
            Log.d("MyTag", "And finally....")
      }
}

Not much of a difference right? No, but did you notice something? There's no throws clause on doSomething(…)! Does Kotlin just do away with with throws clauses? Yes, but Kotlin doesn't just get rid of throws, it gets rid of checked Exceptions all together. This means that the following is completely valid at compile time:

fun doSomething(data : String) {
        if(data.contains("test")) {
            throw IllegalStateException("Test!")
        }
}

fun main() {
        doSomething("testData")
}

However, this results in a RuntimeException now! This is going to be a big gotcha coming from Java, but you might be more comfortable with it coming from Python or C#. Remember how I stated in one of my previous posts how I try to be factual, but state my opinion? Well, it's opinion time: I disagree with this design decision.

More opinion ahead…

The necessity of checked exceptions

My opinion definitely doesn't negate the decisions of the people (who are way smarter than me) who implemented this in Kotlin, but I feel like this is one of those things that Java does (‘tries to’, rather) right. Some of the arguments against checked exceptions are pretty factual and logical in nature; the inability to have checked exceptions in streams and lambda expressions being probably the biggest ones. I personally don't know how to solve these problems, and I know they're not easy problems to solve, but I don't think that these use cases justify a solid argument against all checked exception implementations.

Factual arguments aside, if you go just by Kotlin's Exceptions documentation it looks like their decision and justification against checked exceptions is more because of the verbosity of handling those exceptions and how the exceptions are often mishandled, which I think is a bad call. There's lots of reading on this topic that can be found pretty easily by just googling it if you're unfamiliar with the ongoing debate, so I'm not going to harp on it too much, but I will a little.

Yes, there are a lot of cases where Exceptions are caught generically, or excessively thrown up the chain, polluting the code base with a ton of try{}catch(...){} and throws ...[Exception]statement. I hate seeing this as much as anyone, but as someone who has taken over many large projects developed by people with widely varying levels of knowledge and experience, I would rather deal with seeing Exception handling done verbosely than not seeing it at all. Kotlin's page quotes Bruce Eckel as stating (paraphrasing) exception checking is good for small programs, bad for big programs. I argue the exact opposite.

Strict discipline and full knowledge of the APIs you're using are required to properly develop solutions without checked exceptions. The problem is, developers rarely have the time to understand every minute detail of code written by someone else. The best of us will make a mistake based on an assumption that someone else's code does x when it actually does y, and if the best of us make mistakes, hopefully you're keeping an eagle eye on your summer interns and new hires as well as reading the source code of all the libraries you're using. I'd much rather use try/catch/throws everywhere than hope that the simplest or most common of Exceptions doesn't cause a runtime exception because I didn't know to handle it and no one else noticed it before production.

On a side note, it probably won't surprise you by this point, but if you've ever seen how Go handles errors, I love the way they handle errors. While it is one of the biggest complaints many people have with Go, and it really isn't feasible to implement in Kotlin or Java, I really think Go does this right by idiomatically using errors as a return return value. Yes it's more verbose, but its also more explicit, you know what your program is supposed to do, and what it may do if something goes wrong. By being more explicit we're at least able to see where an error/exception can occur and decide how or if we're going to handle it. Hopefully in future releases of Kotlin we'll at least see a compile warning for an unhandled exception, but it doesn't look like there are any plans in the near future.

That all said, this is mostly a matter of opinion. Every team and use case is going to be different. This is a decision you and your team MUST consider when looking at Kotlin. It has its differences from Java, and while most of them are positive, the negatives might outweigh the needs of your team. Don't assume Java is dead or that any other option is off the table, you need the right tool for the job and you're the one who needs to decide which tool to use.

comments powered by Disqus