5 common Kotlin mistakes and how to avoid it (as a Java Developer)
1.) Using Java’s Lamba function syntax:
This is one of the common mistakes Java developers make while using Kotlin. While using filter, map, forEach etc. they often tend to use the below syntax:
val fruits = listOf("apple", "banana", "apricot")
val fruitsWithA = fruits.stream().filter{ fruit -> fruit.startsWith("a") }
This code is going to work as Kotlin is designed to work alongside with Java but this should not be used as Kotlin has its own syntax and there is potential performance overhead for using Java’s streams.
The above piece of code can be written like this:
val fruits = listOf("apple", "banana", "apricot")
val fruitsWithA = fruits.filter{ it.startsWith("a") }
2.) Usage of CompleteableFuture: For Asynchronous programming in Java often CompleteableFuture is used. To translate this to Kotlin, many developers make the mistake of using CompleteableFuture to achieve Asynchronous programming.
Kotlin provides coroutines and async functions by default, which could be used for Asynchronous programming.
import java.util.concurrent.CompletableFuture
fun main() {
// Start an asynchronous task
val fetchData = CompletableFuture.supplyAsync {
println("Fetching data...")
Thread.sleep(1000) // Simulate delay
"Data from API"
}
// Transform the data
val processData = fetchData.thenApply { data ->
println("Processing data...")
data.uppercase()
}
// Consume the result
processData.thenAccept { result ->
println("Final result: $result")
}
// Wait for the tasks to complete
processData.join()
println("Program finished!")
}
Kotlin’s in-built functions can be used to achieve the same result, as shown in the example:
import kotlinx.coroutines.*
fun main() = runBlocking {
// Start an asynchronous task
val fetchData = async {
println("Fetching data...")
delay(1000) // Non-blocking delay
"Data from API"
}
// Transform the data
val processData = async {
val data = fetchData.await()
println("Processing data...")
data.uppercase()
}
// Consume the result
println("Final result: ${processData.await()}")
println("Program finished!")
}
3.) Not using inferred typing : Kotlin provides another important feature which is not utilised properly, which is inferred typing. Here is a nomal example of inferred typed function in Kotlin:
fun main() {
// Function with inferred return type
fun greetUser(name: String) = "Hello, $name!"
// No explicit type for the variable; type is inferred
val greeting = greetUser("Alice")
println(greeting) // Output: Hello, Alice!
}
Java developers normally write this function as shown below:
fun main() {
fun greetUser(name: String) : String {
return "Hello, $name!"
}
val greeting = greetUser("Alice")
println(greeting)
}
From the above example the return type and return statement is not required. The first block of code is more concise and clean to read. Also it promotes type safety.
4.) Verbose null checks : In Java, we often have to worry about null checks. But in Kotlin, we can use in built functions for null checks. Here is an example:
fun main() {
fun greetUser(name: String?) : String {
if(name != null || name != ""){
return "Hello, $name!"
}
}
val greeting = greetUser("Alice")
println(greeting)
}
Instead of this, this above piece of code can be written like below:
fun main() {
fun greetUser(name: String?) : String {
if(!name.isNullOrBlank){
return "Hello, $name!"
}
}
val greeting = greetUser("Alice")
println(greeting)
}
isNullOrBlank function checks is the parameter is null or “”. Again this should be used as this is more readable.
5.) Usage of Java’s Collections: Often Java developers use Collections to define the data types in Kotlin as well. While this works, it should be used as we have Kotlin’s own implementation of data types.
For example:
fun main() {
val javaList = ArrayList<String>()
javaList.add("Apple")
javaList.add("Banana")
javaList.add("Cherry")
println("List: $javaList") // Output: List: [Apple, Banana, Cherry]
}
This could be easily replaced by:
fun main() {
// Idiomatic way to create a list
val list = listOf("Apple", "Banana", "Cherry")
println("List: $list") // Output: List: [Apple, Banana, Cherry]
}