0Pricing
Kotlin Academy · Урок

Иерархии Result с запечатанными классами

Пользовательские типы результата

«Иерархии Result с запечатанными классами» — бесплатный урок Kotlin Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Kotlin Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Kotlin Academy содержит 4 уроков всего.

За пределами встроенного Result

Result в Kotlin хранит при failure только исключение. Часто требуется более подробная типизированная информация об ошибке. Иерархия запечатанных классов позволяет точно описать исходы, возможные в Вашей предметной области.

Запечатанный исход

Определите запечатанный тип с подклассами для success и ошибки. Компилятор знает все варианты, поэтому можно обработать их без пропусков.

sealed class Outcome<out T> {
    data class Ok<T>(val value: T) : Outcome<T>()
    data class Err(val message: String) : Outcome<Nothing>()
}

fun main() {
    val r: Outcome<Int> = Outcome.Ok(5)
    println(r)
}

Исчерпывающий оператор выбора

Поскольку тип запечатан, для оператора when не требуется else, а компилятор проверяет обработку каждого варианта.

sealed class Outcome<out T> {
    data class Ok<T>(val value: T) : Outcome<T>()
    data class Err(val message: String) : Outcome<Nothing>()
}

fun describe(o: Outcome<Int>) = when (o) {
    is Outcome.Ok -> "value " + o.value
    is Outcome.Err -> "error " + o.message
}

fun main() {
    println(describe(Outcome.Ok(9)))
    println(describe(Outcome.Err("boom")))
}

Типизированные варианты ошибок

Представьте разные виды failure отдельными подклассами, каждый из которых хранит соответствующие данные. Это лучше, чем одно непрозрачное исключение.

sealed class Fetch<out T> {
    data class Success<T>(val data: T) : Fetch<T>()
    data class NotFound(val id: Int) : Fetch<Nothing>()
    data class Network(val code: Int) : Fetch<Nothing>()
}

fun main() {
    val r: Fetch<String> = Fetch.NotFound(42)
    println(r)
}

Обработка каждого варианта

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

sealed class Fetch<out T> {
    data class Success<T>(val data: T) : Fetch<T>()
    data class NotFound(val id: Int) : Fetch<Nothing>()
    data class Network(val code: Int) : Fetch<Nothing>()
}

fun handle(f: Fetch<String>) = when (f) {
    is Fetch.Success -> "data: " + f.data
    is Fetch.NotFound -> "missing id " + f.id
    is Fetch.Network -> "net error " + f.code
}

fun main() {
    println(handle(Fetch.Network(503)))
}

Приём с Nothing

Варианты ошибок, не содержащие значения success, используют Outcome<Nothing>. Поскольку Nothing является подтипом каждого типа, а обобщённый параметр объявлен как out, ошибка подходит для любого Outcome<T>.

sealed class Res<out T> {
    data class Ok<T>(val v: T) : Res<T>()
    object Empty : Res<Nothing>()
}

fun get(flag: Boolean): Res<String> =
    if (flag) Res.Ok("hi") else Res.Empty

fun main() {
    println(get(false))
}

Object для вариантов без состояния

Если вариант ошибки не содержит данных, объявите его как одиночный объект object, а не как класс.

sealed class Login {
    data class Success(val user: String) : Login()
    object WrongPassword : Login()
    object Locked : Login()
}

fun main() {
    val r: Login = Login.WrongPassword
    println(r === Login.WrongPassword)
}

Возврат иерархии

Функции возвращают запечатанный тип; вызывающий код должен учесть каждый исход, что исключает забытые пути обработки ошибок.

sealed class Parsed {
    data class Num(val value: Int) : Parsed()
    data class Bad(val input: String) : Parsed()
}

fun parse(s: String): Parsed {
    val n = s.toIntOrNull()
    return if (n != null) Parsed.Num(n) else Parsed.Bad(s)
}

fun main() {
    when (val r = parse("x")) {
        is Parsed.Num -> println(r.value)
        is Parsed.Bad -> println("bad: " + r.input)
    }
}

Запечатанный тип и встроенный Result

Используйте встроенный Result для быстрого перехвата исключений. Пользуйтесь собственной запечатанной иерархией, когда Вам нужны несколько типизированных вариантов ошибок с данными предметной области и обработкой всех случаев.

Проектирование иерархии

Хороший дизайн запечатанного результата:

  • Один вариант success, содержащий полезную нагрузку
  • Один подкласс для каждого существенного режима failure
  • Вариантность out T и Nothing для случаев, содержащих только ошибку

Компилятор как средство защиты

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

Быстрая проверка

Проверьте, насколько хорошо Вы понимаете иерархии результатов на основе запечатанных типов.

Итоги

Вы создали иерархии результатов на основе запечатанных типов:

  • Success и типизированные варианты ошибок
  • Исчерпывающий when без else
  • out T и Nothing для случаев, содержащих только ошибку
  • Обработка новых вариантов под контролем компилятора

Далее: функциональная обработка ошибок с помощью map и recover.

Часто задаваемые вопросы

Урок «Иерархии Result с запечатанными классами» бесплатный?

Да — полный текст урока «Иерархии Result с запечатанными классами» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Kotlin Academy, подпишись на CoddyKit PRO. Курс Kotlin Academy содержит 4 уроков всего.

Чему я научусь в уроке «Иерархии Result с запечатанными классами»?

Пользовательские типы результата Ты практикуешь Kotlin Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Kotlin Academy?

Предыдущий опыт не требуется. Kotlin Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.

Сколько времени занимает урок «Иерархии Result с запечатанными классами»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Kotlin Academy?

Да. Каждый урок Kotlin Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Тип Result
  2. runCatching
  3. Иерархии Result с запечатанными классами
  4. Функциональная обработка ошибок
← Назад к Kotlin Academy