Why Kotlin and not Java?
Let me tell you 2 great reasons why you should get into Kotlin. I’m a Sun Certified Programmer for the Java 2 Platform, I even have the badge and certificate to prove it, but that was all long ago. Java was quite exciting when it came out and of course there is a huge volume of Java code, and it remains a popular general purpose language. But the age of Java is really beginning to show — so what of Kotlin?
Why would I suggest you abandon Java in favour of Kotlin, or even Scala? There are quite a few reasons to make the jump. Transitioning to functional Scala properly is a huge step, but the step to Kotlin is much easier. Some IDEs will even help you transform existing Java code straight to Kotlin, but you will probably find snags if you do that, it’s better to develop in Kotlin from scratch.
Java syntax is verbose, Kotlin is elegant by comparison, functions are also first class citizens in Kotlin, and Kotlin supports immutable reference assignments and tail recursion — Scala also has these benefits. There are already plenty of articles you can Google to find out more about the pros and cons of Java vs Kotlin* which I will not dig into here, because all else being equal, Kotlin is the superior language.
*suggested reading; Comparison to Java (Kotlinlang), Java vs. Kotlin (Baeldung)
The goals of software engineering are to produce software that is; 1) maintainable, 2) dependable, 3) efficient and 4) usable. Kotlin has the distinct edge on the first 2 points, and this is BIG!
Let’s take a look at how Kotlin helps us with point 2, developing dependable code, before we get to the main issue of point 1.
You cannot have spent long as a Java developer before you bump into the infamous null pointer exception - analysis has found it to be the number one cause of failure in production code(1). With poorly tested code, this exception may arise and can bring a service down and that could ruin the user experience. When service failures occur and that damages a businesses reputation, revenue may well be lost. So, serious issue.
In Java an object reference can contain either a pointer to an object or it can have the null value i.e. be unassigned. When code depends on these references without checking for assignment first, the null pointer exception will be thrown and depending on how that is handled, bad things may ensue.
Now, you are probably thinking, ‘no problem — just add guard conditions, use Optional, try-catch and some more unit tests’ or some other defensive tricks. But why? Why have to add unit tests, use Optional or guard conditions or try-catch (and you should not catch null pointer exceptions) all over your code to handle a fundamental problem with the language? Note that Optional isn’t bullet proof because an Optional reference can still be null and auto unboxing is also a hidden source of bugs. The cope around dealing with null values is painful(2), and despite all these efforts I still see the null pointer exception shipping to production.
Writing tests consumes time that time would be better spent delivering application code instead. When developing in Java you must have 100% coverage reports including all possible combinations of nullable inputs to guarantee a bug free runtime — a method with 3 input references requires adding 7 additional clear box test cases (2³ minus 1 — ouch!). Who really does this amount of unit testing on a large codebase and achieves 100% coverage with all these possible input combinations? This is not a sound basis for good software engineering.
In Kotlin, the handling of nullable values is explicit and will always be visibly exposed in the code. All but the superficial input layers of code can declare types that will not accept null values unless they really need to, and then the issue will be controlled. A significant source of bugs and inefficiency has been taken away. You can be more confident about delivering dependable code and clients will appreciate the added stability and reduced development time.
Scala has various ways for dealing with nulls, this depends on which version is used. Scala version 3 introduces more robust null handling making it similar to Kotlin, but still not as robust.
Now that the users experience is safer, let’s return to point 1 and consider code readability and thus maintainability, because we want happier developers as well. We said Java was verbose, let’s dig into that.
Kotlin is more concise and more expressive than Java and that means that code volume is reduced and yet potentially more readable. It follows that there will be less scrolling and less to remember as you browse easier to read code. As developers spend around 90% of their time reading other peoples code, less code and more expressive syntax means there is a productivity gain.
Here is the code for calling a FizzBuzz function in Java and then in Kotlin which has a cool when operator. You could produce more compact versions of the solutions in both languages, but the code would not convey the concept well, so comprehension suffers. Note that Java is constantly evolving and new versions bring in ideas from other languages — but the implementation may still be awkward and lack the elegance of Kotlin.
If you are developing some light-weight code like a trivial RESTful API for a micro-service, then Java can do an acceptable job and this particular point may be moot. But, should you have to start scaling up the code to handle complex business logic, or a more complicated component architecture, then Java may make it challenging to create easily comprehensible code.
If you have worked on large Java code bases, you may bump into some oddly named methods with even odder class names — what is going on? This issue of Javas strictly OO approach has been dubbed ‘the kingdom of nouns’ by Steve Yegge(3) — please do read his rant, but note it is dated. The issue can tend to be particularly challenging for developers for whom English is not their first language. More recent version of Java that benefit from streams and lambdas are much better, but still not as slick as Kotlin.
Javas strictly OO approach insists that every behaviour is implemented as a member of a class, but this is not always a sensible proposition. When we develop real software we are not always talking about birds or mammals (noun) that move (verb), like the training material suggests. In the world of software, there are abstracts that don’t fit into that kind of real world box and don’t have real worldly kinds of nouns. We adapt words to try and address this, but why always have those nouns?
In Java we have to do something like this: ThingyDoer.do, but what else would a ThingyDoer do other than do? In Kotlin we can define a ThingyDoer in terms of a stand-alone type with a function signature and pass that around. Java can have cumbersome syntactic ceremony — the Java code will overcomplicate the concept and you will have to wrap everything up with a class or interface. As per the null pointer exception problem, an additional burden is being created that may end up creating difficult to manage code simply because of language limitations.
Kotlin helps us deal with more sophisticated software architectures because it elegantly supports lambdas, high order functions and stand-alone functions. Using this capability applications can have their own elegant Domain Specific Language which much more cleanly expresses the higher order concerns of the application.
In addition to the above, there are also excellent functional extensions to Kotlin such as the Arrow library(4) that add functionality that is almost unthinkable in the ‘Kingdom of Java’. This enables development of more sophisticated functional style applications without having to dive deeper into Scala.
Kotlin does come with some of its own peculiarities, like the scope functions and coroutines, but on the whole it is not that much harder to pick up than Java and in so many ways, developing will be easier.
To play devils advocate, we ought to conclude by asking why you want to prefer Java over Kotlin? The main reasons to stick with Java would be the number of developers that know Java already, and its C-like syntax which is similar to some other languages — one has to take this into account before transitioning. However, if you are seeking excellence, Kotlin has the upper hand.
- The Top 10 Exception Types in Production Java Applications — Based on 1B Events, Alex Zhitnitsky
- 10 Tips to Handle Null Effectively, Grzegorz Ziemoński
- Execution in the Kingdom of Nouns, Stevey’s Blog Rants
- Arrow, Functional companion to Kotlin’s Standard Library