Kotlin中的错误处理:try/catch 与更多方法
错误处理是编写可靠健壮代码的重要组成部分。与 Java 一样,Kotlin 提供了经典的 try/catch/finally 机制,但该语言还提供了更现代的函数式方法。本文将深入探讨 Kotlin 中处理异常的所有方式:从基础的 try/catch 到优雅的 runCatching 以及强大的 Result 和 sealed class。
1. 经典的 try/catch/finally
Kotlin 中异常处理的基本语法与 Java 几乎相同,但有一个重要区别:try 可以作为一个表达式使用,这意味着它可以返回一个值。
示例 1:try 作为表达式
fun parseIntOrNull(input: String): Int? { return try { input.toInt() } catch (e: NumberFormatException) { null }}
fun main() { val result = parseIntOrNull("123") println(result) // 123
val badResult = parseIntOrNull("abc") println(badResult) // null}示例 2:多个 catch 块
fun processInput(input: String) { try { val number = input.toInt() val result = 100 / number println("Result: $result") } catch (e: NumberFormatException) { println("Error: invalid number format") } catch (e: ArithmeticException) { println("Error: division by zero") } finally { println("The finally block always executes") }}finally 块在任何情况下都会执行——无论执行成功还是发生异常。这对于释放资源(关闭文件、连接)非常方便。
2. Elvis 运算符 (?:) 与安全调用
Kotlin 提供了一种优雅的方式来处理 null 值,这通常有助于避免繁琐的 try/catch 结构。
示例:使用 Elvis 运算符进行安全转换
fun safeStringToInt(input: String): Int { return input.toIntOrNull() ?: 0 // 如果为 null,则返回 0}
fun main() { println(safeStringToInt("42")) // 42 println(safeStringToInt("abc")) // 0}toIntOrNull() 函数是 Kotlin 内置的 try/catch 替代方案。它不会抛出异常,而是在出错时返回 null。Elvis 运算符 ?: 用于提供一个默认值。
3. 函数式方法:runCatching 和 Result
现代 Kotlin 推荐使用函数式风格进行错误处理。核心工具是 runCatching 和 Result 类。
示例:runCatching
import kotlin.Result
fun divide(a: Int, b: Int): Result<Int> { return runCatching { a / b }}
fun main() { val successResult = divide(10, 2) val failureResult = divide(10, 0)
// 处理结果 successResult .onSuccess { println("Success: $it") } // Success: 5 .onFailure { println("Error: ${it.message}") }
failureResult .onSuccess { println("Success: $it") } .onFailure { println("Error: ${it.message}") } // Error: / by zero
// 使用默认值获取结果 val value1 = successResult.getOrDefault(-1) // 5 val value2 = failureResult.getOrDefault(-1) // -1
// 转换结果 val mappedResult = successResult.map { it * 2 } // Result(10)}runCatching 的优点:
- 代码更具声明性和可读性。
- 无需手动编写
try/catch。 - 结果可以轻松地通过调用链(
.map等)进行组合、转换和处理。