PCG logo
Article

Kotlin: Pitfalls, Tips & Tricks

Introduction

In the realm of modern software development, the battle between Java and Kotlin continues to captivate programmers. We at PCG have had the opportunity to discuss the learning curve with Kotlin at a webinar, particularly for those coming from a Java-based development background. The seasoned consultants that we are, we have been documenting this journey and decided to bring forth some more tips, tricks and pitfalls, which may help out fellow developers on their journeys to transition from Java to Kotlin. In this article, we will dive into the Kotlin-versus-Java saga and unveil some insights into navigating this exciting shift.

Pitfalls

Compatibility with Jackson

Jackson is a popular JSON serialization/deserialization library used in Spring. Kotlin data classes, which are commonly used for modeling data in Kotlin, may have different default behavior with Jackson serialization compared to Java classes without the usage of the module 'jackson-module-kotlin’External Link. For example, consider the following Kotlin data class:

Code Copied!copy-button
data class MyData(val id: Int, val name: String)

By default, Kotlin Data classes generate equals, hashCode, and toString methods that consider all properties, including val properties. However, Jackson uses these generated methods during serialization, which may result in unexpected behavior. To resolve this issue, you can use Jackson’s annotations, such as @JsonIgnore or @JsonProperty, to customize the serialization behavior of Kotlin data classes:

Code Copied!copy-button
data class MyData(
@JsonProperty("id") val id: Int,
@JsonProperty("name") val name: String
)

Static Methods in Kotlin

In Kotlin, static methods and fields are defined inside a companion object, which is similar to a Java static inner class. In the example below, we may expect that the Kotlin compiler will compile the ‘isThisStatic’ method defined inside a companion object as a static method under the hood, but this is not the case. The companion object is formed as a static inner class, but the methods inside are simple instance methods and not static themselves. This may cause issues when using a mixed Java and Kotlin codebase, as method calls which are expected to be static won’t compile.

Code Copied!copy-button
// Kotlin and Java
class MyClass {
    companion object {
        fun isThisStatic() { ... }
        val staticField = ...
    }
}

// Won't compile
MyClass.isThisStatic()

Kotlin provides the ‘@JvmStatic’ annotation to expose the methods and fields in the companion object as static methods and fields in Java. The ‘@JvmStatic’ annotation can only be applied to methods and not to properties. If you want to expose a property as a static field in Java, you need to define a getter method in the companion object. So the following will now compile:

Code Copied!copy-button
// Kotlin and Java
class MyClass {
    companion object {
		@JvmStatic
        fun isThisStatic() { ... }
        val staticField = ...
    }
}

// Good to go
MyClass.isThisStatic()

IDE Converters

Use of IDE converters to convert Java code into Kotlin always needs double checks. The following snippet is an example from IntelliJ:

Code Copied!copy-button
// Java
class Foobar{
    private final Foo foo = new Foo();;
    void doFoo(){
        if (foo != null)
            foo.bar()
    }
}

// Kotlin: Converted by IDE
class Foobar{
    private var foo: Foo? = null;
    fun doFoo(){
        if (foo != null)
        	foo!!.bar()
    }
}

// Kotlin: What would be ideal
class Foobar{
	private val foo: Foo;
    fun doFoo(){
		foo.bar()
	} 
}

Mocking Libraries

Classes and methods in Kotlin are final by default, and anyone using Mocks during testing needs to know that Mockito used to have problems with that. Mockito was primarily built for Java testing and until recently you couldn’t use it for Kotlin fully as it would expect you to write your classes and methods as ‘open’External Link. This has recently changed with the new featureExternal Link introduced by Mockito, but for those using older versions when working with Kotlin, you would need to either use another mocking library OR skip mocking at all.

Code Copied!copy-button
open class B {
  open fun provideValue() = "b"
}

In case you want to avoid this, we recommend using MockkExternal Link. It provides a pure Kotlin-mocking DSL for writing similar tests and is well suited as it provides better support for Kotlin features along with mocking capabilities for final classes and methods.

Null Safety for Platform Types

Despite boosting in-built null safety, Kotlin still is unable to identify null-related issues when you have a mixed code base of Java and Kotlin. For example, the following code will not throw any compile time issues in Kotlin:

Code Copied!copy-button
fun main(){
	val aValue: Int = AJavaClass.generatedNullValue()
}

You would need to handle this as you would in Java:

Code Copied!copy-button
fun main(){
	val aValue: Int = AJavaClass.generatedNullValue() ?: -1
 	val bValue: Int? = AJavaClass.generatedNullValue() 
}

Tips and Tricks

Smart Casting

Smart Casting in Kotlin allows you to NOT specify the type of variable before accessing the property itself. (No more sonar lint warnings when doing type conversions)

Code Copied!copy-button
// Java
Object givenObj = "Something";
if(givenObj instanceof String) {
    // Explicit type casting
    String str = (String) givenObj;
    System.out.println("length of String " + str.length());
}

// Kotlin
val givenObject: Any = "Something"
if(givenObject is String) {
	// Works automatically
	givenObject.substring(...)
}

Elvis Operator is your friend

The Elvis operator (?) provides a way to write more intuitive and stream based code which is more focused on the task at hand and aids readability.

Code Copied!copy-button
// Java
val image = fetchById(1)
if(image != null) {
	...
} else {
	throw NotFoundException("Image doesn't exist")
}

// Kotlin
val image = fetchById(1)
image?.let{
	...
} ?: throw NotFoundException("Image doesn't exist")

Function Types and Interfaces

In Kotlin, functions are first-class citizens, which means they can be treated like any other data type, such as integers, strings, or objects. A function type represents the signature of a function, including its parameter types and return type. This allows you to pass functions as arguments to other functions, return them from functions, and store them in variables or data structures. This functional programming approach allows for more flexible and expressive code, making it easier to work with complex logic and operations.

Code Copied!copy-button
// Function Types
val sayHello = {"Hello from Tom and Gopal"} // corresponds to function signature:  () -> String
println(sayHello())

println({Cheers}()) // Anonymous Function
println{Cheers}() // You can skip brackets in last argument

// Functional Interfaces
callFoo(object: Foo {
 override bar(s: String){
	println(s)
 }
})

callFoo{print(it)}

KTor

KTor is a library built from the ground up using Kotlin and co-routines that is very easy to utilize for building asynchronous client server applications. Getting a fully functional lightweight and flexible microservice up and running can be as simple as:

Code Copied!copy-button
fun main() {
	embeddedServer(Netty, port = 8000) {
		routing {
			get ("/") {
				call.respondText("Hello, world!")
			}
		}
	}.start(wait = true)
}

Summary

“In the grand tapestry of programming languages, Kotlin stands as a vibrant thread reshaping the fabric of modern development.” - ChatGPT

In this post, we presented the wisdom gained from using Kotlin in live production grade applications. By embracing them, developers can harness Kotlin’s advantage over Java. So what’s next?

We would suggest first time users go through the official Kotlin documentationExternal Link to familiarize themselves with the language and continue pushing to learn more.

If you found this article useful, do share it with your friends. If you have more tips, tricks and gotcha’s, please let us know. We’d be happy to learn more about it. If you want to hear more from us and about Kotlin, check out the recording of our webinar “Modernize Your Java Applications with Kotlin: Best Practices and Strategies”External Link

Thanks for reading!


Continue Reading

News
PCG Showcases Cutting-Edge AI Solutions at FAIEMA 2024

PCG presented AI innovations at FAIEMA 2024, featuring document retrieval and road monitoring solutions using AWS Cloud. Speakers included Thanasis Politis and Vasko Donev, along with industry experts.

Learn more
Article
AWS Lambda: Avoid these common pitfalls

It's a great offering to get results quickly, but like any good tool, it needs to be used correctly.

Learn more
Article
Google Cloud report uncovers: GenAI as a driver of growth and success

The study ‘The ROI of Generative AI’ by Google Cloud delivers impressive figures. Find out how organisations around the world benefit from GenAI.

Learn more
Case Study
Sports
How TVB Stuttgart organizes its home games with Asana

With the work management tool, the German handball league benefits from efficient collaboration and increases employee satisfaction.

Learn more
See all

Let's work together

United Kingdom
Arrow Down