Clean Code in Kotlin Clean Code in Kotlin

Page content

In this post, we’ll learn how to write clean code in Kotlin with some practical examples and make our code more concise and readable

Use when

Use when expression, when you have more than two conditions. You can replace any code snippet having if-else condition with more concise when expression. It is similar to switch operator in Java.

// Don't
fun getCategoryByEatableName(eatable: String): String {
    if (eatable == "mango" || eatable == "banana" || eatable == "apple") {
        return "FRUIT"
    }else if (eatable == "potato" || eatable == "tomato" || eatable == "onion") {
        return "VEGETABLE"
    }else if(eatable == "milk" || eatable == "curd") {
        return "DAIRY"
    }else {
        return "UNKNOWN"
    }
}

// Do
fun getCategoryByEatableName(eatable: String) =
    when(eatable) {
        "mango", "banana", "apple" -> "FRUIT"
        "potato", "tomato", "onion" -> "VEGETABLE"
        "milk", "curd" -> "DAIRY"
        else -> "UNKNOWN"
    }

Use also

The also function doesn’t mutate the returned output from the previous function and it comes handy in printing the output for debugging.

Example 1

We want to multiply and print the returned output, which make our method verbose like this:-

fun multiplyAndPrint(a: Int, b: Int): Int {
    val c = a * b
    println(c)
    return c
}

var out = multiplyAndPrint(2, 3)
// out = 6

Let’s improve above function where multiply only do the multiplication and also take care of printing the returned output

fun multiply(a: Int, b: Int) = a*b

var out = multiply(2, 3).also { println(it)}
// out 6

Even if you call another function add from also expression, it doesn’t mutate the returned output of multiply

fun multiply(a: Int, b: Int) = a*b
fun add(a: Int, b: Int) = a+b

var out = multiply(2, 3).also { add(it, 4)}
// out is still 6, not 10
Example 2

The also function is quite useful in the streams as well, where it doesn’t mutate the collection. It comes quite handy where you want to debug the output in the chain of stream operations

val l = (1..10).toList()

// Don't
l.filter{ it % 2 == 0 }
	.map {
		println(it)
		it * it
	}

// Do
l.filter{ it % 2 == 0 }
	// Prints, but doesn't mutate the collection
	.also { println(it) }
	.map { it*it}

Use let

Use let along with ?. null-safe (or elvis) operator, when you want to do a null check

// Don't
val user: User? = findUser()
if (user != null){
    println(user.name)
}

// Do
findUser()?.let { println(it.name) }
//or
findUser()?.name?.let(::println)

Use apply

You can use apply to initialize a mutable object. It returns the same object it operates on and comes handy in initializing the properties.

class Server {
    lateinit var host: String
    var port: Int = 0
    var numOfInstance: Int = 1
    var isCloudInstance: Boolean = true
}

// Don't
var server = Server()
server.host = "sg1234"
server.port = 8080
server.numOfInstance = 3

// Do
var server = Server().apply {
    host = "sg1234"
    port = 8080
    numOfInstance = 3
}

Initialize Map with values

Immutable map is initialized using mapOf and mutable map is initialized using mutableMapOf in Kotlin.

Example 1

Use shorthand to operator to map “key” to “value” instead of initializing Pair each time

// Don't
val immutableMap = mapOf(
    Pair("A", 1),
    Pair("B", 2),
    Pair("C", 3))

val mutableMap = mutableMapOf(
    Pair("A", 1),
    Pair("B", 2),
    Pair("C", 3))

// Do
val immutableMap = mapOf(
    "A" to 1,
    "B" to 2,
    "C" to 3
)

val mutableMap = mutableMapOf(
    "A" to 1,
    "B" to 2,
    "C" to 3
)
Example 2

Map is also quite useful in Kotlin logging

var url = "http://localhost:8080/users"
var method = "GET"
var status = 200

logger.debug(mapOf("url" to url, "method" to method, "status" to status))

Multiline String

An easy way to create a multiline String is to wrap it in """.

// Don't
println("Twinkle, Twinkle Little Bat\n" +
        "How I wonder what you're at!\n" +
        "Up above the world you fly,\n" +
        "Like a tea tray in the sky.\n" +
        "Twinkle, twinkle, little bat!\n" +
        "How I wonder what you're at!")

// Do
println("""
        Twinkle, Twinkle Little Bat
        How I wonder what you're at!
        Up above the world you fly,
        Like a tea tray in the sky.
        Twinkle, twinkle, little bat!
        How I wonder what you're at!""")

Extension Function

Extension functions is very powerful feature in Kotlin to add new functionality in existing class such as String without even inheriting it. We cannot do such things in Java and generally write a StringUtil class to do that.

// Don't
object StringUtil {
    fun capitalize(string: String): String{
        return string.replaceFirstChar { it.uppercaseChar() }
    }
}

println(StringUtil.capitalize("ashish"))
// Ashish

// Do
fun String.capitalize(): String {
    return replaceFirstChar { it.uppercaseChar() }
}

println("ashish".capitalize())
// Ashish