Kotlin

Рассмотрим особенности использования выражения when в Kotlin.

Базовый случай использования

На фрагменте ниже приведен базовый случай использования выражения when:

fun myFunction(valueToTest: Any) {
    when (valueToTest) {
        is MyType1 -> doSomething1()
        is MyType2 -> doSomething2()
    }
}

Компилятор выдает предупреждение, поскольку это неисчерпывающая реализация. То есть все возможные случаи не рассмотрены. При установлении типа Any для “valueToTest” можно получить безграничное количество случаев для обработки.

Для устранения предупреждения компилятора добавьте случай else:

fun myFunction(valueToTest: Any) {
    when (valueToTest) {
        is MyType1 -> doSomething1()
        is MyType2 -> doSomething2()
        else -> doSomethingElse()
    }
}

От предупреждения к ошибке

Особенностью Kotlin является то, что некоторые ключевые слова (if/else и when) возвращают значения. Однако что делать компилятору, если нужно получить значение вне неисчерпывающего выражения when? Похоже, это выражение уже получено. Предупреждение из первого примера становится ошибкой. Это означает, что программа больше не компилируется. Возьмем первый пример:

fun myFunction(valueToTest: Any): Boolean {
    return when (valueToTest) {
        is MyType1 -> true
        is MyType2 -> true
    }
}

Здесь добавлен тип return к myFunction, а также ключевое слово return перед выражением when. Для компиляции кода нужно добавить случай else, как и при удалении предупреждения:

fun myFunction(valueToTest: Any): Boolean {
    return when (valueToTest) {
        is MyType1 -> true
        is MyType2 -> true
        else -> false
    }
}

Этот пример компилируется. Однако при передаче параметра Any теряется та самая фича, которой посвящена статья.

Фича, говоришь?

Все верно. Добавим немного кода в пример. Нам понадобится класс sealed или enum (поведение с when одинаковое):

enum class MyEnum {
    CASE1,
    CASE2
}

MyEnum содержит два случая, которые представлены объектами Kotlin (singletons). Таким образом, исчерпывающее when можно использовать без случая else:

fun myFunction(valueToTest: MyEnum): Boolean {
    return when (valueToTest) {
        CASE1 -> true
        CASE2 -> true
    }
}

Вот и все! Компиляция прошла успешно. Однако чем именно хорош этот код? Все дело в поддержке. Представьте, что к работе над проектом присоединяется новичок. Что произойдет, если он добавит новый случай CASE3?

enum class MyEnum {
    CASE1,
    CASE2,
    CASE3
}

fun myFunction(valueToTest: MyEnum): Boolean {
    return when (valueToTest) {
        CASE1 -> true
        CASE2 -> true
    }
}

Код, содержащийся в myFunction, больше не компилируется! Таким образом, новичок точно знает, где нужно обработать новый случай.

Пример в Android

В этом примере реализуется класс sealed, с помощью которого обрабатываются различные типы строк в Android. Строка может быть источником (с типом Int и ссылкой на id ресурса) или простой строкой.

sealed class AndroidString {
    data class Resource(val resource: Int) : AndroidString()
    data class Plain(val str: String) : AndroidString()

fun toString(resources: Resources): String {
        return when (this) {
            is AndroidString.Resource -> resources.getString(resString)
            is AndroidString.Plain -> str
        }
    }
}

В этом примере показано создание чистого кода с классами enums/sealed и выражением when. Также можно добавить еще один класс в AndroidString для обработки случая plural strings.

Заключение

Выражение when ускоряет и упрощает поддержку приложений. Оно помогает избежать ошибок при росте приложения при условии использования меньшего количества случаев else.

Спасибо за внимание!


Перевод статьи Alexandre Crettet: Kotlin when Expression