Cleaner Code with Kotlin
This post will cover using features provided by Kotlin to improve a codebases' formatting, readability, and structure. This isn’t a post on how to learn the thought school of clean code, but rather how to take advantage of what is provided to work with what you already know about clean code. If you want to learn more about clean code and how to apply it, you can pick up a copy of “Clean Code” by Robert Martin. This book is an essential for any software engineer.

Less code
Less code is better. There’s less to maintain, read through, and of course, to write. If you’re coming from a Java background to Kotlin you will gain many benefits around this.
Models
A common contributor to boilerplate code in Java are models, classes whose purpose is to encapsulate data for passing around the system. Codebases can often have many different models and it can be cumbersome to create them, although this is less so with tools now built into IDEs.
public class Person {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
As you can see from the example, providing the getters and setters required can get repetitive and creates an overhead when you want to change any of the members. Kotlin provides functionality to help implement the model pattern whilst reducing these issues with data classes.
Marked with the data
keyword, data classes remove having to specify getters and setters by automatically generating them when compiling for the JVM. The model example we had before now looks like this when using a kotlin data class:
data class Person(val name: String, val age: Int)
If you need to customise a members getter or setter, you can still achieve this:
data class Person(val name: String, val age: Int) {
val displayName: String
get() = name.capitalize()
}
You can learn more about data classes here.
Scopes
If you’ve ever had to configure an object you’ll know how aesthetically unpleasing or awkward it can potentially get.
Consider this Java example of configuring a recycler view for Android, a common use case that you’ll have to learn if you ever want to display lists of data in an application:
private RecyclerView recyclerView;
private RecyclerView.Adapter configuredAdapter;
private void configureRecyclerView() {
recyclerView = (RecyclerView) findViewById(R.id.recycler_view_id);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(configuredAdapter);
recyclerView.setHasFixedSize(true);
}
We have to hold onto a reference of a recycler view we need to find, then use that reference to configure the recycler view. While it may not seem like this example is too noisy, compare how you would do this with Kotlin:
private lateinit var configuredAdapter: RecyclerView.Adapter
private fun configureRecyclerView() {
recyclerViewId.apply {
layoutManager = LinearLayoutManager(context)
adapter = configuredAdapter
setHasFixedSize(true)
}
}
Using the apply
receiver, we can reference the scope of the object we are applying on, removing the need to use the reference prefix.
You’ll also notice that we no longer hold onto a reference of the recycler view and use the find by id pattern. With the kotlin android extensions available, we can use view bindings to do this for us.
You can learn more about kotlin android extensions and how to enable them here.
There are more Kotlin scopes available to help you reduce the strain on object referencing, improving the readability of the code. You can learn more about these scope functions here.
Easier test coverage
If you’re familiar with clean code then you will know that tests are important. So important, that you should be writing them first before you write any production code. If that statement adds to more confusion, then you really should pick up that copy of the book.
Given a class that has a helper function to check an enum value and return some checks based on the value passed in, you may be inclined to write 2 tests. One that tests true when the enum member supplied is the one you are looking for, and one that tests false when the enum member supplied is not the one you are looking for. You may think these 2 tests satisfy the test coverage, but what about all the other enum members you didn’t cover? What if a future engineer adds a new case to the helper function? What if someone adds a new enum member and forgets to add a new test?
enum class Movie {
STAR_WARS,
LION_KING,
JURASSIC_PARK;
}
fun isAnimated(movie: Movie) = movie == LION_KING
@Test
fun `should not be animated movie when movie is not lion king`() {
val movie = Movie.STAR_WARS
assertThat(target.isAnimated(movie), `is`(false))
}
@Test
fun `should be animated movie when movie is lion king`() {
val movie = LION_KING
assertThat(target.isAnimated(movie), `is`(true))
}
If we were to add new movies and update isAnimated
to the following:
enum class Movie {
STAR_WARS,
LION_KING,
JURASSIC_PARK,
WALL-E,
ET;
}
fun isAnimated(movie: Movie) = movie == LION_KING || movie == WALL-E
Our tests would still pass and we wouldn’t even have test coverage for the new movie that isn’t concerned with the check in isAnimated
.
Using some kotlin features, we can ‘Kotlinfy’ these tests to make them more robust for the future:
@Test
fun `should not be animated movie when movie is not lion king`() = withMovieNotLionKing { movie ->
assertThat(target.isAnimated(movie), `is`(false))
}
private fun withMovieNotLionKing(action: (Movie) -> Unit) =
Movie.values()
.filter { it != LION_KING }
.forEach { action(it) }
Using the collectins extensions and expression body functions, we can create a test helper to filter out the undesired value and make sure all the other values are covered. Now, when a new value that is added that doesn’t meet the check, we can be assured that it is still covered by our tests.
Abstracted concurrency
Concurrency is difficult to do right and trying to do clean concurrency is even more difficult. Fortunately, Kotlin provides a way to simplify concurrency with Kotlin coroutines.
Kotlin coroutines is not part of the standard Kotlin API, but a first party library developed by the creators of Kotlin. The reason it’s not included is due to allowing users the ability to use whatever concurrency library they want.
When writing concurrency code, reserved keywords are required like await
or async
, or you may have to utilise callbacks. This can create a block of code like this:
fetchClientData(args,
storeClientData(args,
notifySuccess(args, {
service.await()
})
})
})
With coroutines, you can use the suspend
keyword in the function declaration to turn concurrency calls into sequential calls:
suspend fun updateClient() {
fetchClientData()
storeClientData()
notifySuccess()
}
Each call in this function deals with some sort of awaiting, but no special syntax around the call is required, as long as suspend
is present.
Coroutines help reduce the noise often associated with concurrency and makes it easy to use and understand. You can learn more about Kotlin Coroutines here.