Either<L, R>: типизированная обработка ошибок без исключений
Используйте Either, чтобы представлять успешное и неудачное выполнение без выбрасывания исключений
«Either<L, R>: типизированная обработка ошибок без исключений» — бесплатный урок Kotlin Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Kotlin Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Kotlin Academy содержит 4 уроков всего.
Проблема использования исключений в бизнес-логике
Исключения предназначены для неожиданных сбоев (нулевой указатель, ошибка IO). Использование их для обозначения ожидаемых ошибок бизнес-логики (ошибка валидации, «не найдено») делает поток управления невидимым, вынуждает добавлять try/catch в месте каждого вызова и скрывает ошибки в сигнатурах функций.
Что такое Either<L, R>
Either — это тип-сумма с двумя вариантами: Left(value: L) по соглашению содержит ошибку, а Right(value: R) — значение успешного результата. Функция, возвращающая Either, явно указывает возможность сбоя в своей сигнатуре.
Either в Arrow
Добавьте Arrow в свой проект, чтобы получить Either и всю экосистему его операторов:
// build.gradle.kts
implementation("io.arrow-kt:arrow-core:1.2.4")
// Usage
import arrow.core.Either
import arrow.core.left
import arrow.core.rightВозврат Either из функции
Возвращайте value.right() при успехе и error.left() при ошибке. Вызывающий код должен обработать обе ветви:
sealed class UserError { object NotFound : UserError(); data class InvalidEmail(val msg: String) : UserError() }
fun findUser(id: Long): Either<UserError, User> =
if (id <= 0) UserError.NotFound.left()
else User(id, "Alice").right()Обработка Either с помощью fold
Используйте fold(ifLeft, ifRight), чтобы обработать оба варианта в одном выражении:
val result = findUser(1L)
val message = result.fold(
ifLeft = { error -> "Error: $error" },
ifRight = { user -> "Found: ${user.name}" }
)
println(message)Преобразование значения Right с помощью map
map { } преобразует значение Right, не затрагивая Left. Благодаря этому Either является функтором — преобразования можно безопасно объединять в цепочку:
val nameResult: Either<UserError, String> = findUser(1L).map { it.name }Объединение в цепочку с помощью flatMap
flatMap { } объединяет в цепочку вычисления, которые сами возвращают Either. Если какой-либо шаг возвращает Left, цепочка завершается досрочно, а ошибка передаётся дальше без выполнения последующих шагов:
fun validateEmail(email: String): Either<UserError, String> =
if (email.contains("@")) email.right()
else UserError.InvalidEmail("bad format").left()
fun createUser(email: String): Either<UserError, User> =
validateEmail(email).flatMap { validEmail ->
User(1L, validEmail).right()
}Either.catch для оборачивания исключений
Either.catch { } выполняет блок и оборачивает любое исключение в Left<Throwable>. Используйте его на границе между устаревшим кодом, который выбрасывает исключения, и доменом на основе Either:
val result: Either<Throwable, User> = Either.catch {
userRepository.findOrThrow(id)
}Восстановление с помощью getOrElse и orElse
getOrElse { default } извлекает значение Right или возвращает значение по умолчанию. orElse { alternativeEither } заменяет Left другим вычислением:
val user: User = findUser(0L).getOrElse { User(-1L, "Guest") }
val user2: Either<UserError, User> = findUser(0L).orElse { User(99L, "Default").right() }Сопоставление с образцом с помощью when
Используйте when для запечатанного класса, подобного Either, или непосредственно для Either с помощью is Either.Left / Either.Right, чтобы исчерпывающе обработать все варианты:
when (val r = findUser(1L)) {
is Either.Left -> println("Error: ${r.value}")
is Either.Right -> println("User: ${r.value.name}")
}Either в сервисных слоях
Возвращайте Either из функций репозитория и сервиса. Преобразуйте его в HTTP-ответы на уровне маршрута Ktor, сохраняя бизнес-логику свободной от деталей фреймворка:
get("/users/{id}") {
val id = call.parameters["id"]?.toLongOrNull() ?: return@get call.respond(HttpStatusCode.BadRequest)
userService.findById(id).fold(
ifLeft = { call.respond(HttpStatusCode.NotFound) },
ifRight = { call.respond(it) }
)
}Быстрая проверка
Что делает flatMap { } со значением Either.Left?
Повторение: Either<L, R>
Основные выводы:
Eitherявно указывает путь обработки ошибки в сигнатуре функцииRight= успех;Left= ошибка (по соглашению)map { }— преобразовать успешное значение;flatMap { }— объединить в цепочку вычисления, которые могут завершиться ошибкойfold(ifLeft, ifRight)— обработать оба варианта в одном выраженииEither.catch { }— обернуть код, выбрасывающий исключения, на границах системы
Изучай Kotlin с ИИ-репетитором — бесплатно
Пиши и запускай код прямо в браузере, получай мгновенную помощь от ИИ-репетитора 24/7 и продолжи учиться на сайте или в приложении.
- Курсы
- 51
- Уроки
- 203
Часто задаваемые вопросы
Урок «Either<L, R>: типизированная обработка ошибок без исключений» бесплатный?
Да — полный текст урока «Either<L, R>: типизированная обработка ошибок без исключений» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Kotlin Academy, подпишись на CoddyKit PRO. Курс Kotlin Academy содержит 4 уроков всего.
Чему я научусь в уроке «Either<L, R>: типизированная обработка ошибок без исключений»?
Используйте Either, чтобы представлять успешное и неудачное выполнение без выбрасывания исключений Ты практикуешь Kotlin Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Kotlin Academy?
Предыдущий опыт не требуется. Kotlin Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.
Сколько времени занимает урок «Either<L, R>: типизированная обработка ошибок без исключений»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Kotlin Academy?
Да. Каждый урок Kotlin Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Either : типизированная обработка ошибок без исключений
- DSL Arrow Raise: объединение типизированных ошибок
- Option и Nullable: когда использовать каждый из них
- Функциональное моделирование предметной области с базовыми типами Arrow