Kotlin : A Java developer's perspective

A simple list of features from my first hands-on experience on a project combining Java and Kotlin
23.03.2023
Tags

Introduction

My oldest memory of Java is having a look at the first edition of “Java : The Complete Reference” by Herbert Schildt bought by my sibling. As I went through it, I developed a fascination for computer science and later went on to get my bachelor’s degree in the subject. As a fresh graduate, my initial organization played a critical role in me becoming a Java developer. It wasn’t a decision driven by personal choice, but by a client’s requirements. Fast forward numerous years and Java-based projects ahead (varying from Java 5 to 11, which I really started to enjoy), I recently came across a brownfield project that utilized Kotlin. Like all developers, I suffer from a few biases towards Java (an interesting article on the same is here), but driven by the client’s requirements once again, I decided to give it a try. This article tries to summarize a few features of Kotlin that have made my life easier along with the challenges I faced as I was implementing a production grade application in it for the first time.

The Benefits

Data classes: The ‘data’ keyword in Kotlin allows you to initialize a data class that is meant to hold some data. Unlike a ‘Plain Old Java Object’ where you would have to define the getters, setters, equals and hashcode methods, the addition of a simple keyword ‘data’ would automatically stereotype this class and generate the required methods for you:

data class Link(
    var link: String,
    val type: LinkType
)

This removes the need to add the ‘Project Lombok’ library (and going through a phase of installing it in your Eclipse IDE; old school devs know what I am talking about) and speeds up development time. Java 14 added a new feature of ‘Records’ which allows you to do the same, but it doesn’t offer a ‘copy’ method to ease your object creation and also enforces the ‘final’ keyword for variables making them immutable.


‘var’ and ‘val’ keywords: Kotlin allows us to infer the type of a mutable variable with the ‘var’ keyword as follows:

var name = "John Doe"

and if we want to initialize it later we can provide a type to it and do so:

var name : String
name = "John Doe"

Java 10 introduced the ‘var’ identifier, which allows similar capabilities to infer the type, but it needs to be initialized on the spot and can’t be initialized later.

The following would be unacceptable in Java:

var name;
name = "John Doe";

Bear in mind that in Kotlin ‘var’ is a keyword, whereas in Java it is an identifier meaning you can still have variable names such as ‘var’.

Finally, to make it immutable, you would need to add the ‘final’ keyword in Java, but Kotlin removes this need by allowing you to declare immutable fields with the ‘val’ keyword as below:

// This is immutable
val name = "John Doe"

‘object’ keyword for singletons: Kotlin allows you to make a singleton class as with the ‘object’ keyword. The following class would have only one instance as the Kotlin compiler would make the constructor private, create a static reference and initialize it in a static block under the hood.

object DataSource{
	fun init(){
	// Omitted for brevity
	} 
}

Infix functions: Some functions in Kotlin can be called without using ‘.’ and ‘()’. This makes it sound similar to natural language when you are coding and helps you visualize your work better. An example of the same can be seen here in the definition of a ‘Map’ :

var paramNamesToReplaceMap: Map<String, String> = mapOf(
        "&&&" to "&",
        "?&&" to "?",
        "&&" to "&",
        "?&" to "?"
    )

Here the ‘to’ represents an Infix function that returns a Pair.


‘it’ shorthand in Lambda expressions: A shorthand of a lone argument that we can pass to a lambda expression in Kotlin is ‘it’. The availability of this allows you not to have to think about the type of variable available in the expression and write code faster. The snippet below illustrates the same:

// Longhand declaration of variable
list.forEach { item -> println(item) }

// Shorthand declaration
list.forEach { println(it) }

Extension functions: These are special functions in Kotlin that you can utilize to add functionality to existing classes without the need for any inheritance or decorator patterns. After defining this function we can use it as if it was a part of the class from the beginning and even the IDE offers it in the autocomplete:

fun String.extractDomainNameFromUrl() : String {
    // Omitted for Brevity
}

// This can now be used on any String as follows
val domainName = "http://www.example.com?param=abc".extractDomainNameFromUrl()

Prior to this, you would need a separate utility class that allows you to do the same.


Null Safety: By default when you use a variable in Kotlin, it assumes that it can’t be null. You simply can’t do the following:

var name: String = null

This will throw a compilation error in Kotlin and forces the developer to think about whether this value can be null or not. You can still do this to have null values, but it forces you to shift your mindset to think about immutability and make your code safer (while avoiding the usage of ‘Optional’):

var name: String? = null


My key takeaway while highlighting the pros of Kotlin is brevity. Java code is verbose whereas Kotlin is concise and removes the need for writing down boiler plate code. This makes it naturally easy to read, less prone to errors and allows you to write significantly fewer lines of code to achieve a task. The list above is not comprehensive, but tries to provide an indication towards the same. Brevity also comes at a cost. Too much of it can also lead to unmanageable code. What is important here is to focus on the potential that Kotlin can offer.

The Costs

Confusing operators: In regard to null safety, you can see that the ternary operator in Java ‘?’ has a completely different meaning in Kotlin. The null safety further opens up a conversation on how to handle null conditions when you want them specifically for a use case. Kotlin uses safe calls, which when chained together excessively, can lead to confusing code in the code base that can easily scare Java developers away. An example:

item?.let { res = res.plus(it); it }?.also{it -> println("Value: $it")}

The code above checks if the ‘item’ variable has a non-null value. If yes, then it adds it to the result and then also prints this non-null value. This is not very readable at the first glance and imagine a chain of such calls all together. Plus, handling every nullable reference in this way can be very cumbersome as well.


No Checked Exceptions: There are no checked exceptions in Kotlin. Many developers believe that checked exceptions aren’t great. This is because some developers will just catch and dismiss them without handling them properly. This leads to hidden problems in the codebase. The developers who oppose using checked exceptions believe that not using this feature properly is worse than not having it at all. Personally, I believe that they help in making the code more robust and documenting what can lead to method failure. A specific example from the project would be the usage of ‘UnsupportedEncodingException’ when encountering URLs from the internet and external sources, which could have been avoided before the project went live.


Mixture with Java: Kotlin and Java are inter-operable with each other. This means you can have Java and Kotlin classes in the same project. While this makes it easier to use the features of both languages together, it also makes it difficult to maintain as you need to keep track of where your Java code ends and where the Kotlin part starts. This is true for large brownfield development projects where you need to have solid guidelines to help keep this in check. An example would be to use Kotlin for only new features and Java for what is already written. It is easy to say that we will use the best of both worlds, but there is also a possibility that we use the worst of both, making your code even more difficult to understand and debug.


Support: Introduced in 1995, Java is almost as old as me, whereas Kotlin was first released in 2016. This means that the Java developer community is also much larger than that of Kotlin, making peer support more available and there are many older projects written in Java. We can see in some of the examples above, that new functionality has been introduced into later Java versions, for which Kotlin had been used to solve. There are also some divergences such as using ‘JPAStreamer’ which allows you to return a stream of database results, and helping resolve performance issues with queries that return large datasets. It can’t be used with Kotlin because of the clash between it’s usage of the ‘?’ operator vs. Kotlin’s usage of the ‘?’ for null safety. This could have really benefitted the project we were doing.

Summary

Based on my personal experience, any programming language, library or framework can turn costly if utilized just for the sake of using it. You shouldn’t use a screwdriver for hammering nails or a hammer for screwing screws - maybe you can in some magical way, but you shouldn’t. Similarly, solving the business problem at hand should always have first priority; the technology behind it should be thought of as a tool that comfortably allows you to do so.

In this post, I highlighted a few pros and cons of using Kotlin from a first time user’s perspective. So what’s next? I would suggest first time users go through the official Kotlin documentation to familiarize themselves with the language and the various changes from Java that may seem daunting in the beginning. The possibility from there on is endless!

If you found this article useful, do share it with your friends. If you have more pros and cons that you feel are relevant for any first time user to know, please let me know. I’d be happy learn more about it.

Thanks for reading!