# Error Handling: Exceptions and Result — Kotlin

Source: https://www.skillbyai.com/en/kotlin/p-errors

> try as an expression, require, check and runCatching.

## No checked exceptions

Kotlin has **no checked exceptions**: you are not forced to catch or declare them. `try` is an expression, so `val n = try { s.toInt() } catch (e: NumberFormatException) { 0 }` works. Standard preconditions are `require(cond)` (throws `IllegalArgumentException` for bad arguments), `check(cond)` (throws `IllegalStateException` for bad state) and `error("msg")`. Many functions have safe variants such as `toIntOrNull()`. `Result<T>` with `runCatching { }` wraps success or failure as a value, with `getOrNull`, `getOrElse`, `map`, `onSuccess` and `onFailure`. Note that `runCatching` also catches `CancellationException`, so be careful using it inside coroutines. For domain errors, a sealed result type is often clearer.

## Errors, generics and Java interop

Real projects need clear error handling, reusable generic code and smooth interop with Java libraries.

![Three ideas: exceptions and Result, generics with variance, Java interoperability.](assets/figures/kotlin/section-7-map.svg) — Figure 7.1 — Kotlin code calling Java and Java calling Kotlin.

## Preconditions, Result and a sealed outcome

Three styles side by side.

```kotlin
fun parsePort(raw: String): Int {
    val port = raw.toIntOrNull()
    require(port != null && port in 1..65_535) { "invalid port: $raw" }
    return port
}

fun readPort(raw: String): Int =
    runCatching { parsePort(raw) }
        .onFailure { println("falling back: ${it.message}") }
        .getOrElse { 8080 }

sealed interface Transfer {
    data class Done(val id: String) : Transfer
    data class Rejected(val reason: String) : Transfer
}

fun transfer(amount: Long, balance: Long): Transfer =
    if (amount <= balance) Transfer.Done("tx-1") else Transfer.Rejected("insufficient funds")
```

## Exceptions for bugs, types for outcomes

Use exceptions for programming errors and unexpected failures; model expected business outcomes, such as "insufficient funds", as values in a sealed type so callers must handle them.

**Quiz:** What does `check(condition)` throw when the condition is false?

- [ ] Nothing; it only logs
- [ ] `IllegalArgumentException`
- [ ] `NullPointerException`
- [x] `IllegalStateException`

*Answer:* `IllegalStateException`. require is for arguments, check is for state.
