From Java to Kotlin: Lessons Learned
I’ve worked in software development for 17 years. I work with Big Data, microservices and distributed systems. I primarily code in JVM-based languages (e.g., Java, Scala, Kotlin). Recently, our team worked on a client project using Kotlin.
Kotlin is an open-source statically-typed programming language developed by JetBrains. The project began in 2010 and the current version is 1.5.10, published on May 24, 2021. Kotlin is free, with source code available on GitHub.
Our client provides a distributed trade finance platform that uses blockchain technology. In this post, I’ll detail how our team successfully delivered the project using Kotlin.
Key features of Kotlin
The first and most important feature of Kotlin, in my opinion, is null-safety. Within the Kotlin context, there is no way to get a Null Pointer Exception. This is different from Java, in which Null Pointer Exceptions can still occur. Without having to worry about Null Pointer Exceptions, you can write less protective code and there are significant savings in terms of inspections.
Another feature is immutability. Kotlin supports “val/var,” or separate interfaces for mutable/immutable collections. When a variable is announced, it says it is not changeable and, accordingly, if we know that our object is immutable this eliminates a lot of side effects.
We can be sure that by passing the object inward, no one will change any property of it. There are mutable and immutable collections and it’s easy to trace what’s going on.
Another benefit is coroutines. If we need to work with asynchronous multi-flow code often, coroutines make life easier, as they let you get rid of callbacks.
When handling events, you write asynchronous code as if it were synchronous. There is quite a bit of magic going on here, but it allows you to write complex synchronous code in an easy and readable manner.
There are also extension functions, which allow you to extend the behavior of virtually any class. We can write any additional function either for a string or for our own type, which is quite convenient.
There are smart casts, where we don’t need to specify a type — the type is inferred by Kotlin automatically. There is no separation, unlike Java on raw types. It doesn’t matter whether “Integer” starts with an uppercase or lower case letter.
We just have an “int” type and Kotlin chooses whether it’s possible to use a regular type or to create an object. As a result, we write much less code. When we need to box something, we don’t need to call an install integer and so on.
Challenge: growing and scaling a team of Kotlin engineers
We faced a challenge on this client project: we needed to deliver a project using Kotlin with few Kotlin engineers on the team. We did, however, have talented Java engineers who were willing to re-skill on Kotlin.
We tackled this challenge in two steps. The first step was to prepare a Kotlin starter course for our engineers. For Java engineers looking to re-skill in Kotlin, the subject matter of this course was expected to be easily understandable.
We tried to minimize the number of new technologies on the project and we purchased books and tutorials that help the team. These resources were useful:
- The Kotlin documentation site
- The book “Kotlin In Action” by Dmitry Jemerov and Svetlana Isakova
- The book “The Joy of Kotlin” by Pierre-Yves Saumont
The Kotlin docs site describes what features are in the language, with examples. It also provides an opportunity to open an online playground and start writing something, to try something out yourself without installing anything.
It was important for new team members to understand that Kotlin is closer to Scala than pure Java. We wanted them to understand the concepts of immutability and null-safety. We started out with strict code reviews. Two engineers were responsible for reviewing code, leaving comments and advising the rest of the team how the code could be improved.
The second step was meeting once a week where we discussed what was accomplished and what was done well. We were able to train a large number of engineers in just one month. Soon enough, we were able to remove the restriction of strict code reviews and the pace of pull requests picked up immediately.
The initial project delivery was a big success and we continue to work with the client today.
With Kotlin, we generated less boilerplate code. If we analyze the examples included in Corda, which has both Kotlin and Java code, we see that the Kotlin code is considerably shorter. Because of Kotlin’s null-safety, we didn’t need to guard against or respond to Null Pointer Exceptions. This leads to a lower number of errors. The immutable tables meant that we had fewer problems with related side effects.
There is an impression that Kotlin inherited common features from other programming languages. For example, null safety is present in C# and Scala. Immutability is inherent in Scala. Lambda expressions are present in Scala and C#. Kotlin combines the best and most effective features from these languages, but at the same time, is less complex. It allows us to write code easier and faster.
We get a much higher speed of development with Kotlin. If I were to start a new project, I would choose Kotlin. It allows me to write much less code than in Java, and it’s not as complicated as Scala. With Scala, the qualification of engineers must be considerably higher.
Thank you, Kotlin.
Q&A on Kotlin
This blog post is based on an online “Tech Talk” that I presented. Below are questions that were asked by attendees, as well as my answers.
Q: How difficult was the transition to Kotlin — did anyone burn out?
A: Principally, no. The whole crew is still with us, for the most part. We were honest during the process of hiring — we said that there would be a requirement to learn Kotlin. And in our case not just Kotlin, but Corda as well. What we did was ask the most experienced engineers on our team to help the new hires learn Kotlin.
We had tutorials, training and recommended resources to review. During code reviews, we provided feedback on how to rewrite the code. I wouldn’t say everything was 100% smooth, but in the end, the result was good. Everyone stayed with us and no one quit.
Q: Were there cases involving legacy code in Java?
A: No. Our project is new and it was on Kotlin. Talking about interoperability between Java and Kotlin, I only encountered one situation: spin batch did not start right away. The rest worked just fine. When I am talking about print batch, there was a definite issue with serialization/deserialization of data classes by default, so the data classes feature was not working. This was fixed quite easily. As for the rest, we had no issues. Our project was fully targeting Kotlin.
Q: Were there any problems with debugging?
A: No. The project is completely on Kotlin. We had no inconveniences due to the lack of primitives because there is really no big difference. During compilation, Kotlin decides whether we need a primitive type or a class. If we need a primitive type, then the only thing that we’re doing is assigning values. It participates in compilation. It will realize that we need a primitive type. It will announce it. Therefore, we had no issues with this.
Q: Did we have problems with interoperability between Java and Kotlin?
A: No, because our project is completely on Kotlin, but I am aware that there are minor problems when it does not work properly. In particular, I saw articles that said that we can get null values, the same as in Kotlin. I also saw articles saying that some components don’t start right away because there are data classes; however, all of these issues can be resolved.
Q: How correctly does the conversion of Java code to Kotlin work and what amount of manual effort was required?
A: It always works correctly. There are just a few cases when the code doesn’t work after the conversion. If you write code in Java and then convert it to Kotlin, it will still appear that it was written using Java.
If you were using some mutable collectors or an approach where you change the value of some variables (i.e., mutate the data), the language will translate it in the same manner. You may get a situation that in about 99.9% of cases it can convert Java code to Kotlin. It will work in most cases but will appear as if it was written in Java.