Обработка ошибок

Учимся обрабатывать сбои, не давая программе аварийно завершиться.

Исключение (exception) — объект, описывающий ошибку времени выполнения; его «бросают» при сбое и «ловят», чтобы обработать.

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

try / catch / finally

Код, способный бросить исключение, помещают в try. Блок catch ловит ошибку, а finally выполняется в любом случае.

fun main() {
    try {
        val n = "abc".toInt()   // бросит исключение
        println(n)
    } catch (e: NumberFormatException) {
        println("Не число!")
    } finally {
        println("Готово")
    }
}

Вывод:

Не число!
Готово

try как выражение

Как и if/when, try в Kotlin — выражение и возвращает значение. Это позволяет аккуратно подставить запасной результат.

fun parseOrZero(s: String): Int {
    return try {
        s.toInt()
    } catch (e: NumberFormatException) {
        0
    }
}

fun main() {
    println(parseOrZero("42"))
    println(parseOrZero("oops"))
}

Вывод:

42
0

Бросаем собственные исключения

Своё исключение бросают оператором throw. Часто это делают при недопустимых аргументах.

fun divide(a: Int, b: Int): Int {
    if (b == 0) throw IllegalArgumentException("Деление на ноль")
    return a / b
}

fun main() {
    println(divide(10, 2))
    try {
        divide(5, 0)
    } catch (e: IllegalArgumentException) {
        println("Поймано: ${e.message}")
    }
}

Вывод:

5
Поймано: Деление на ноль

Как работает под капотом

Важное отличие от Java: в Kotlin нет проверяемых исключений (checked exceptions). Компилятор не заставляет объявлять или ловить исключения — это решение остаётся за вами. Когда исключение брошено, выполнение текущего блока прерывается и управление «всплывает» вверх по цепочке вызовов до ближайшего подходящего catch. Если такого нет, программа завершится с ошибкой. Удобная альтернатива исключениям для ожидаемых сбоев — возвращать nullable-результат или sealed-тип с вариантами Success/Error.

Частые ошибки

  • Глотать исключение пустым catch. Ошибка исчезнет незаметно — как минимум залогируйте её.
  • Ловить слишком общий тип. Лучше ловить конкретное исключение, а не Exception вообще.
  • Использовать исключения для обычной логики. Для ожидаемых случаев предпочтительнее nullable или sealed-результат.

Итог

  • Рискованный код оборачивают в try, ошибку ловят в catch, finally выполняется всегда.
  • try — выражение и может возвращать значение.
  • Собственное исключение бросают через throw.
  • В Kotlin нет проверяемых исключений; для ожидаемых сбоев удобны nullable и sealed-типы.
Проверьте себя
1. Когда выполняется блок finally?
AТолько при успехе
BТолько при ошибке
CВ любом случае: и при ошибке, и без неё
DНикогда
2. Чем особенны исключения в Kotlin по сравнению с Java?
AИх нельзя ловить
BВ Kotlin нет проверяемых (checked) исключений
CИх всегда нужно объявлять
DОни работают медленнее
3. Как вернуть 0 при неудачном разборе строки в число?
AИспользовать !! после toInt()
BОбернуть toInt() в try-выражение с catch, возвращающим 0
CПрименить элвис-оператор к строке
DЭто невозможно