Kotlin tips: Singleton, Utility Functions, group Object Initialization and more…
Tips on writing good Kotlin code and using what the language has to offer
There are many benefits of using Kotlin; it is concise, safe and most importantly it is 100% interoperable with Java. It also tries to solve some of the limitations of Java. Check out this great document that Jake Wharton wrote back in 2015 on why Square should start using Kotlin.
Now is a good time to consider Kotlin for your next big feature or project, because Google finally announced it to be a first-class language for writing Android apps in Google I/O 17.
So here are some tips to help you start making use of what the language has to offer and writing good Kotlin code.
Singleton
Implementing lazy loaded and thread-safe singletons in Kotlin is really easy, unlike Java where you would have to rely on the complex double-checked locking pattern. And although Java enum singletons are thread-safe, they are not lazy loaded.
object Singleton {
var s: String? = null
}
Contrary to a Kotlinclass
, an object
can’t have any constructor, but init
blocks can be used if some initialization code is required.
Singleton.s = "test" // class is initialized at this point
The object will be instantiated and its init
blocks will be executed lazily upon first access, in a thread-safe way.
Utility Functions
Favor Kotlin top-level extension functions over the typical Java utility classes. And for easier consumption within Java code, use @file:JvmName
to specify the name of the Java class which would get generated by the Kotlin compiler.
// Use this annotation so you can call it from Java code like StringUtil.
@file:JvmName("StringUtil")fun String.lengthIsEven(): Boolean = length % 2 == 0val lengthIsEven = "someString".lengthIsEven()
Object Initialization
Use apply
to group object initialization statements to allow for cleaner, easier to read code.
// Don't
val textView = TextView(this)
textView.visibility = View.VISIBLE
textView.text = "test"// Do
val textView = TextView(this).apply {
visibility = View.VISIBLE
text = "test"
}
Some more small tips just for you!
let()
Using let()
can be a concise alternative for if
. Check out the following code:
val listWithNulls: List<String?> = listOf("A", null)
for (item in listWithNulls) {
if (item != null) {
println(item!!)
}
}
With let()
, there is no need for an if
.
val listWithNulls: List<String?> = listOf("A", null)
for (item in listWithNulls) {
item?.let { println(it) } // prints A and ignores null
}
when()
when
replaces the switch operator of Java and it can also be used to clean up some hard to read if conditions.
// Java
public Product parseResponse(Response response) {
if (response == null) {
throw new HTTPException("Something bad happened");
}
int code = response.code();
if (code == 200 || code == 201) {
return parse(response.body());
}
if (code >= 400 && code <= 499) {
throw new HTTPException("Invalid request");
}
if (code >= 500 && code <= 599) {
throw new HTTPException("Server error");
} throw new HTTPException("Error! Code " + code);
}
And in Kotlin it would look like:
// Kotlin
fun parseResponse(response: Response?) = when (response?.code()) {
null -> throw HTTPException("Something bad happened")
200, 201 -> parse(response.body())
in 400..499 -> throw HTTPException("Invalid request")
in 500..599 -> throw HTTPException("Server error")
else -> throw HTTPException("Error! Code ${response.code()}")
}
Read-only lists, maps, …
Kotlin distinguishes between mutable and immutable collections (lists, sets, maps, etc). Precise control over exactly when collections can be edited is useful for eliminating bugs, and for designing good APIs.
The Kotlin standard library contains utility functions and types for both mutable and immutable collections. For example listOf()
, and mutableListOf()
.
val list = listOf(“a”, “b”, “c”)val map = mapOf("a" to 1, "b" to 2, "c" to 3)
Lazy property
lazy
is good, use it when it makes sense! Keeping a low memory footprint is a good use case, and also saving CPU cycles if its initialization is expensive.
Lazy properties only get computed upon first access.
val str: String by lazy {
// Compute the string
}
Fight the temptation to squeeze everything in a single expression
It can be quite tempting to try to squeeze everything into a single expression. Just because you can, doesn’t mean that it’s a good idea.
If a single expression function is forced to wrap to a new line it should be a standard function.
Aim for readable and clean code instead.
Curious? Here are some more materials to check out
A complete reference to the Kotlin language can be found here.
Are you curious yet? Try Kotlin!
Advanced Kotlin tips is now available, check it out at http://bit.ly/advanced-kotlin-tips
If you liked this article make sure to 👏 it, and follow me on Twitter!