Создание пользовательских классов исключений
Определяйте предметные исключения с понятными сообщениями и свойствами.
«Создание пользовательских классов исключений» — бесплатный урок Kotlin Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Kotlin Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Kotlin Academy содержит 4 уроков всего.
Зачем нужны собственные исключения
Собственные исключения передают смысл предметной области: вызывающий код может перехватывать их отдельно, получать полезные сообщения и по-разному реагировать в зависимости от типа.
Простейшее собственное исключение
Унаследуйте класс от Exception (или от конкретного подкласса, например RuntimeException) и передайте сообщение конструктору суперкласса.
class InvalidEmailException(message: String) : Exception(message)
fun main() {
try {
throw InvalidEmailException("missing @")
} catch (e: InvalidEmailException) {
println("caught: ${e.message}")
}
}Исключение со свойствами
Добавляйте поля для передачи контекста: HTTP-код состояния, имя поля, число повторных попыток и так далее.
class HttpError(
message: String,
val statusCode: Int
) : Exception(message)
fun main() {
try {
throw HttpError("Not Found", 404)
} catch (e: HttpError) {
println("HTTP ${e.statusCode}: ${e.message}")
}
}Исключение с причиной
Передайте cause, чтобы сохранить исходное исключение в цепочке.
class DatabaseError(message: String, cause: Throwable) : Exception(message, cause)
fun main() {
try {
try { error("connection refused") }
catch (e: Throwable) { throw DatabaseError("query failed", e) }
} catch (e: DatabaseError) {
println("${e.message} (caused by: ${e.cause?.message})")
}
}Проверяемые и непроверяемые исключения
Kotlin рассматривает все исключения как непроверяемые (объявление throws не требуется). По соглашению создавайте подклассы RuntimeException.
class ValidationException(field: String, message: String) :
RuntimeException("[$field] $message")
fun main() {
try {
throw ValidationException("age", "must be positive")
} catch (e: ValidationException) {
println(e.message)
}
}Иерархия исключений предметной области
Постройте иерархию, чтобы вызывающий код мог перехватывать базовый тип для общей обработки или конкретные подтипы для особых случаев.
open class AppException(message: String) : RuntimeException(message)
class NotFoundException(what: String) : AppException("$what not found")
class UnauthorizedException : AppException("not authorized")
fun main() {
val errors = listOf(NotFoundException("user"), UnauthorizedException())
for (e in errors) {
try { throw e }
catch (e: AppException) { println("App error: ${e.message}") }
}
}Запечатанная иерархия исключений
Для закрытого набора типов ошибок используйте иерархию с sealed — вызывающий код сможет обработать каждый вариант без пропусков.
sealed class ApiError(message: String) : RuntimeException(message) {
class Timeout : ApiError("timeout")
class NotFound(val id: String) : ApiError("not found: $id")
class ServerError(val status: Int) : ApiError("server returned $status")
}
fun describe(e: ApiError) = when (e) {
is ApiError.Timeout -> "request timed out"
is ApiError.NotFound -> "id ${e.id} missing"
is ApiError.ServerError -> "5xx: ${e.status}"
}
fun main() {
println(describe(ApiError.NotFound("user-42")))
}Соглашение об именовании
Для ясности заканчивайте имена собственных классов исключений на Exception или Error. Используйте прошедшее время или существительное, например NotFound и InvalidInput.
class InvalidInputException(msg: String) : RuntimeException(msg)
class UserNotFoundException(id: Int) : RuntimeException("user $id not found")
fun main() {
try { throw UserNotFoundException(7) }
catch (e: UserNotFoundException) { println(e.message) }
}Документирование исключений
Используйте KDoc @throws, чтобы документировать исключения, которые может выбросить функция.
/**
* @throws InvalidEmailException if the email is malformed
*/
fun validate(email: String) {
if ("@" !in email) throw InvalidEmailException("missing @")
}
class InvalidEmailException(m: String) : RuntimeException(m)
fun main() {
try { validate("nope") }
catch (e: InvalidEmailException) { println(e.message) }
}Не злоупотребляйте исключениями
Исключения предназначены для исключительных ситуаций. Для ожидаемых результатов (пустых входных данных, отсутствующего необязательного поля) предпочитайте возвращаемые nullable-значения, запечатанные типы результатов или значения по умолчанию.
fun findUser(id: Int): String? = if (id == 1) "Alice" else null
fun main() {
val name = findUser(42) ?: "(unknown)"
println(name) // (unknown), no exception
}Практический пример
Реалистичное собственное исключение для вымышленного клиента API с кодом состояния и телом ответа.
class ApiException(
message: String,
val statusCode: Int,
val responseBody: String
) : RuntimeException(message)
fun main() {
try {
throw ApiException("Bad Request", 400, "{\"error\":\"invalid\"}")
} catch (e: ApiException) {
println("[${e.statusCode}] ${e.message}: ${e.responseBody}")
}
}Быстрая проверка
Какой родительский класс чаще всего используют при определении исключения Kotlin для конкретной предметной области?
Итоги
Собственные исключения передают смысл предметной области. Наследуйте их от RuntimeException или Exception, добавляйте свойства для передачи контекста, сохраняйте причины и создавайте иерархии для групповой обработки. Используйте запечатанные исключения для закрытых наборов и предпочитайте nullable-типы или типы результатов, когда ошибки ожидаемы.
Часто задаваемые вопросы
Урок «Создание пользовательских классов исключений» бесплатный?
Да — полный текст урока «Создание пользовательских классов исключений» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Kotlin Academy, подпишись на CoddyKit PRO. Курс Kotlin Academy содержит 4 уроков всего.
Чему я научусь в уроке «Создание пользовательских классов исключений»?
Определяйте предметные исключения с понятными сообщениями и свойствами. Ты практикуешь Kotlin Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Kotlin Academy?
Предыдущий опыт не требуется. Kotlin Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.
Сколько времени занимает урок «Создание пользовательских классов исключений»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Kotlin Academy?
Да. Каждый урок Kotlin Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- try/catch/finally как выражение
- Создание пользовательских классов исключений
- runCatching и Result
- Повторный выброс и связывание исключений