Как поймать много исключений в Kotlin за раз: полный гайд с кодом

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: почему эта тема взрывает Stack Overflow?

Если вы когда-нибудь писали на Kotlin и сталкивались с необходимостью перехватить несколько разных исключений, вы знаете, что это может быть больно. Особенно если вы пришли из Java, где мульти-кэтч (catch (IOException | SQLException e)) — это стандарт с Java 7. В Kotlin всё иначе, и многие разработчики теряются.

Вопрос "How to catch many exceptions at the same time in Kotlin?" на Stack Overflow собрал тысячи просмотров. И это не случайно: обработка ошибок — это то, с чем сталкиваются абсолютно все. Сегодня мы разберём эту задачу раз и навсегда.

В этой статье вы узнаете:

  • Почему мульти-кэтч в Kotlin работает не как в Java
  • Как красиво и компактно перехватывать несколько исключений
  • Когда использовать when в catch, а когда — старый добрый if
  • Реальные примеры из боевого кода

Поехали!

Проблема: где мой мульти-кэтч?

В Java вы пишете просто:

try {
    riskyOperation()
} catch (IOException | SQLException e) {
    // обрабатываем одинаково
}

Красиво, лаконично, понятно. Но в Kotlin такого синтаксиса нет. Если вы попробуете написать catch (IOException | SQLException e), компилятор скажет вам «нет».

Почему? Потому что Kotlin сделал выбор в пользу явности и чистоты. Разработчики языка решили, что мульти-кэтч с вертикальной чертой — это не совсем стиль Kotlin. Но это не значит, что вы не можете поймать несколько исключений. Просто делается это иначе.

Давайте посмотрим на три основных способа.

Способ 1: множественные блоки catch (самый очевидный)

Самый простой путь — просто добавить несколько блоков catch подряд. Kotlin это поддерживает на ура.

try {
    val data = loadData()
    processData(data)
} catch (e: IOException) {
    println("Ошибка ввода-вывода: ${e.message}")
} catch (e: SQLException) {
    println("Ошибка базы данных: ${e.message}")
} catch (e: Exception) {
    println("Неизвестная ошибка: ${e.message}")
}

Плюсы:

  • Предельно ясно — каждый тип исключения обрабатывается отдельно
  • Можно дать разную логику для разных ошибок
  • Отлично читается новичками

Минусы:

  • Много дублирования кода, если логика обработки одинаковая
  • При 5+ исключениях блок становится громоздким

Способ 2: catch с when (элегантный и гибкий)

Вот где Kotlin раскрывается во всей красе. Вместо мульти-кэтч вы можете использовать when внутри одного блока catch.

try {
    val result = fetchFromNetwork()
    saveToDatabase(result)
} catch (e: Exception) {
    when (e) {
        is IOException -> handleNetworkError(e)
        is SQLException -> handleDatabaseError(e)
        is SecurityException -> logAndNotify(e)
        else -> throw e // перебрасываем, если не знаем как обработать
    }
}

Этот подход — настоящая находка. Он позволяет:

  • Обрабатывать разные исключения в одном месте
  • Использовать все возможности when, включая проверки условий
  • Легко добавлять новые типы исключений

Вот более продвинутый пример с условиями:

try {
    connectToService()
} catch (e: Exception) {
    when {
        e is IOException && e.message?.contains("timeout") == true -> 
            println("Таймаут соединения, пробуем снова")
        e is IOException -> 
            println("Ошибка сети: ${e.message}")
        e is SQLException && e.errorCode == 1045 -> 
            println("Ошибка аутентификации в БД")
        else -> 
            throw RuntimeException("Неизвестная ошибка", e)
    }
}

Когда использовать? Когда у вас 3+ исключения, которые требуют разной, но связанной логики.

Способ 3: sealed классы и пользовательские исключения (для профи)

Если вы проектируете свою архитектуру с нуля, рассмотрите sealed классы. Они позволяют создать иерархию исключений и обрабатывать их на новом уровне.

sealed class AppException(message: String, cause: Throwable? = null) : Exception(message, cause) {
    class NetworkException(message: String, cause: Throwable? = null) : AppException(message, cause)
    class DatabaseException(message: String, cause: Throwable? = null) : AppException(message, cause)
    class AuthenticationException(message: String, cause: Throwable? = null) : AppException(message, cause)
}

// Использование
fun process() {
    try {
        riskyOperation()
    } catch (e: AppException) {
        when (e) {
            is AppException.NetworkException -> handleNetwork(e)
            is AppException.DatabaseException -> handleDatabase(e)
            is AppException.AuthenticationException -> handleAuth(e)
        }
    }
}

Это даёт вам:

  • Полный контроль над типами ошибок
  • Компилятор проверяет, что вы обработали все варианты (если используете when как выражение)
  • Чистую архитектуру без сюрпризов

А как же Kotlin-стиль? Советы от сообщества

На Stack Overflow часто встречается вопрос: «Что считается best practice?». Вот консенсус сообщества:

  1. Не ловите Exception везде. Это антипаттерн. Ловите только те исключения, которые можете обработать осмысленно.
  2. Используйте Result. В Kotlin есть Result<T>, который помогает избежать try-catch вообще:
fun loadData(): Result<String> = runCatching {
    // код, который может выбросить исключение
    fetchFromApi()
}

// Использование
val result = loadData()
result.onSuccess { data -> println("Данные: $data") }
      .onFailure { error -> println("Ошибка: ${error.message}") }

3. Избегайте пустых catch. Никогда не пишите:

catch (e: Exception) {}

Это скрывает ошибки и делает дебаг адом.

Пример из реального проекта: загрузка данных из сети и БД

Давайте соберём всё вместе. Представьте, что у вас есть функция, которая загружает данные из сети, кэширует их в БД и возвращает пользователю. Здесь могут возникнуть разные ошибки: сетевые проблемы, сбой записи в БД, неверный формат данных.

fun loadAndCacheData(): String {
    return try {
        val networkData = fetchFromNetwork() // может быть IOException, SocketTimeoutException                

Блоги

Рекомендаций по книгам