A bunch of books on shelves

IMHO, Kotlin is not about big killer features - although extension methods and properties could certainly be categorized as such, but about a bunch of small improvements that have deep impact. Most of them are not built-in into the language, but are functions offered as part of the Kotlin standard library. In this post, I’d like to go through a limited set of them, and describe how they can be used to improve the code.

TODO()

It’s quite common to have //TODO comments in a new codebase. For most of us developers, it might even be a reflex. When the flow comes in, don’t stop because of a lack of specification, but write down a reminder to get back to it later. Whatever later means. Even IDEs happily generate code with such comments.

fun computeCustomerNumber(customer: Customer): String {
    // TODO Not specified as of November 27th
}

Yet, no problem occurs when the actual code is run. Nothing happens, nothing to really remind us that piece should be implemented. Of course, some code analysis tools might uncover it, but it might already be too late. And it requires the tool to actually be run.

Kotlin provides the TODO() function, that actually throws an exception when it’s called. This way, even running a simple unit test will forcibly point you to the fact there’s something to be done there.

fun computeCustomerNumber(customer: Customer): String {
    TODO("Not specified as of November 27th")
}

apply()

Depending on an API specifics, constructing some objects might be rather tedious, and involve a lot of details. To hide those details, the current consensus is generally to create a method out of it. Here’s a snippet to create a combo-box component for the Vaadin web framework:

fun createCountriesCombo(): ComboBox<String> {
    val countries = ComboBox<String>("Countries")
    countries.setItems("Switzerland", "France", "Germany", "Austria")
    countries.isEmptySelectionAllowed = false
    countries.placeholder = "Choose your country"
    countries.addValueChangeListener {
        val country = countries.value
        bus.post(CountryChangeEvent(country))
    }
    return countries
}

Even then, depending on the amount of properties to set, it’s easy to get lost in the details. apply() is a simple function, defined as:

fun <T> T.apply(block: T.() -> Unit): T { block(); return this }

It means this function can be called on any type T, and its only argument is a lambda receiver, that returns nothing. As for any lambda receiver, this inside the lambda refers to the object the function is called on. This let us refactor the above snippet into the following:

fun createCountriesCombo(): ComboBox<String> {
val countries = ComboBox<String>("Country").apply {
        setItems("Switzerland", "France", "Germany", "Austria")
        isEmptySelectionAllowed = false
        placeholder = "Choose your country"
        addValueChangeListener {
            bus.post(CountryChangeEvent(value))
        }
    }
}

Even better, the snippet can be now be easily refactored to take advantage of expression body:

fun createCountriesCombo() = ComboBox<String>("Country").apply {
    setItems("Switzerland", "France", "Germany", "Austria")
    isEmptySelectionAllowed = false
    placeholder = "Choose your country"
    addValueChangeListener {
        bus.post(CountryChangeEvent(value))
    }
}

Icing on the cake, with any IDE worth its salt, folding may be used to display the overview, and unfolding to reveal the details.

IDE-unfolded apply function
IDE-folded apply function

use()

Before Java 7, the closing of a connection had to be explicitly done in a finally block:

Connection conn = getConnection();
try {
    // Do stuff with the connection
} finally {
    if (conn != null) {
        conn.close();
    }
}

Java 7 added the try-with-resource syntax. For example, the previous snippet can refactored into this one:

try (Connection conn = getConnection()) {
    // Do stuff with the connection
}

The try-with-resource syntax greatly simplifies the code. However, it’s part of the language syntax and thus carries a lot of implicitness:

  • The type in the resource statement must implement AutoCloseable.
  • There can be multiple resources opened in the same statement. In that case, they are closed in the reversed order they were opened.
  • If an exception is thrown in the try block and also during the closing of the resource(s), the latter is suppressed and set to the main exception.

The Kotlin counterpart is handled through the use function, whose signature is:

fun <T : Closeable, R> T.use(block: (T) -> R): R

No black magic involved. It’s a simple function, and its source code is available online.

Conclusion

Those are only a sample of what is available. In general, the syntax of languages can be learned in 21 days (or probably even less). However, it takes a lot more time to know the API.

Kotlin is less about the syntax and more about the API.