Clean Code in Kotlin
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