Kotlin 异常与标准库
1. 异常捕获 try-catch-finally
基础语法
kotlin
// 标准 try-catch-finally
try {
val result = 10 / 0
} catch (e: ArithmeticException) {
println("算术异常: ${e.message}")
} finally {
println("无论是否异常都会执行")
}
// 多异常捕获
try {
parseDate(input)
saveToDb(data)
} catch (e: ParseException) {
println("解析失败: ${e.message}")
} catch (e: DatabaseException) {
println("数据库错误: ${e.message}")
} catch (e: Exception) {
println("未知错误: ${e.message}")
}
// Kotlin 1.7+ 多异常合并(简化)
try {
doSomething()
} catch (e: ParseException | DatabaseException) {
// e 是 ParseException 或 DatabaseException
println("处理错误")
}
try 作为表达式
kotlin
// try-catch 可作为表达式返回值
val result = try {
parseNumber(input)
} catch (e: NumberFormatException) {
0 // 默认值
}
// 完整形式
val finalResult = try {
val n = parseNumber(input)
n * 2
} catch (e: Exception) {
println("Error: ${e.message}")
0
} finally {
cleanup()
}
异常类型与处理原则
kotlin
// Kotlin 异常层次结构
// Throwable -> Error(系统级,不应捕获)
// -> Exception -> RuntimeException(未检查异常)
// -> Checked Exception(已检查异常)
// 原则:已检查异常必须处理或声明抛出
// Kotlin 策略:已检查异常被视为未检查异常(JVM 平台)
// 抛出异常
fun validateAge(age: Int) {
if (age < 0 || age > 150) {
throw IllegalArgumentException("Age must be 0-150, got: $age")
}
}
// Nothing 类型(never returns)
fun fail(message: String): Nothing {
throw IllegalArgumentException(message)
}
异常与空安全
kotlin
// 异常 vs 空安全的选择
// 可预见的业务异常 → 使用 null 或 Result 类型
// 不可预见的系统异常 → 使用 try-catch
// 场景一:文件读取(可预见的异常)
fun readFile(path: String): String? {
return try {
File(path).readText()
} catch (e: IOException) {
null
}
}
// 场景二:解析 JSON(可预见的异常)
fun parseJson(json: String): User? {
return try {
gson.fromJson(json, User::class.java)
} catch (e: JsonSyntaxException) {
null
}
}
// 场景三:assert 与 require(开发期快速失败)
fun register(name: String, age: Int) {
require(name.isNotBlank()) { "Name cannot be blank" }
require(age in 0..150) { "Age must be 0-150" }
// 业务逻辑
}
// check vs require
// require:验证先决条件,失败抛 IllegalArgumentException
// check:验证状态,失败抛 IllegalStateException
2. runCatching 优雅异常处理
runCatching 基础
kotlin
// Kotlin 1.0+ 标准库
import kotlin.runCatching
// runCatching:将代码块包装成 Result
val result = runCatching {
parseNumber("42")
}
result.isSuccess // true
result.isFailure // false
result.getOrNull() // 42
result.exceptionOrNull() // null
val failed = runCatching {
parseNumber("not a number")
}
failed.isSuccess // false
failed.isFailure // true
failed.getOrNull() // null
failed.exceptionOrNull() // NumberFormatException
链式异常处理
kotlin
// 获取值或默认值
val value = runCatching { getConfig("timeout") }
.getOrDefault(3000)
// 获取值或计算默认值
val timeout = runCatching { getConfig("timeout").toInt() }
.getOrElse { ex ->
println("Failed: ${ex.message}")
5000
}
// map:转换成功结果
val name = runCatching { getUserName(userId) }
.map { it.uppercase() }
.getOrNull()
// mapCatching:转换失败结果(保持异常类型)
val transformed = runCatching { riskyCall() }
.mapCatching { anotherRiskyCall(it) }
// recover:恢复失败情况
val data = runCatching { fetchData(id) }
.recover { cachedData(id) } // fetchData 失败时用缓存
.getOrNull()
// recoverCatching:完全控制恢复逻辑
val data2 = runCatching { fetchData(id) }
.recoverCatching { ex ->
println("Failed: ${ex.message}")
cachedData(id) ?: throw ex // 缓存也没有就重新抛出
}
.getOrNull()
runCatching 实战
kotlin
// 场景一:网络请求 + 缓存兜底
suspend fun getUser(id: String): User? {
return runCatching {
api.getUser(id)
}.recoverCatching { ex ->
// 尝试从本地缓存获取
localCache.getUser(id)?.also {
println("Fallback to cache due to: ${ex.message}")
}
}.getOrNull()
}
// 场景二:链式转换 + 异常处理
val displayName = runCatching {
userId
.let { getUserName(it) }
.let { validateName(it) }
.let { formatName(it) }
}.getOrElse { ex ->
println("Name processing failed: ${ex.message}")
"Anonymous"
}
// 场景三:多个可能失败的操作
val (first, second) = Pair(
runCatching { loadFirst() },
runCatching { loadSecond() }
)
val results = listOf(first, second)
.filter { it.isSuccess }
.mapNotNull { it.getOrNull() }
// 场景四:Result 与 sealed class 结合
sealed class Result<out T> {
data class Success<T>(val data: T) : Result<T>()
data class Error(val e: Throwable) : Result<Nothing>()
}
fun <T> runCatchingResult(block: () -> T): Result<T> {
return runCatching(block)
.map { Result.Success(it) }
.exceptionOrNull()
?.let { Result.Error(it) }
?: Result.Success(Unit as T) // unreachable but needed for type
}
Result vs runCatching 选择
kotlin
// Result(标准库):适合简单场景
// runCatching:链式调用更优雅
// 用 Result 的场景
fun parse(input: String): Result<Int> {
return try {
Result.success(input.toInt())
} catch (e: NumberFormatException) {
Result.failure(e)
}
}
// 用 runCatching 的场景
val result = runCatching { parseWithSideEffect() }
.map { it * 2 }
.recover { computeDefault() }
3. 标准库常用工具函数
require / check / assert
kotlin
// require:前置条件检查,失败抛 IllegalArgumentException
fun createUser(name: String, age: Int): User {
require(name.isNotBlank()) { "Name must not be blank" }
require(age in 0..150) { "Age must be 0-150" }
return User(name, age)
}
// check:状态检查,失败抛 IllegalStateException
class LoadingState {
private var data: Data? = null
fun ensureLoaded(): Data {
checkNotNull(data) { "Data not loaded yet" }
return data!!
}
}
// assert:调试断言(只在 JVM -ea 时生效)
fun calculateDivisor(n: Int): Int {
assert(n > 0) { "Divisor must be positive" }
return 100 / n
}
// requireNotNull / checkNotNull / notNull
val name = requireNotNull(userName) { "User must have a name" }
val config = checkNotNull(appConfig) { "Config not initialized" }
val value = notNull(something) { "Something cannot be null" }
let 配合空安全
kotlin
// let 空安全处理
val trimmed = input?.let { it.trim() }?.let { validate(it) }
// let 实现默认值
val len = nullableString?.length ?: 0
// let 简化判空
user?.address?.let { addr ->
addr.city?.let { city ->
println("City: $city")
}
}
// takeIf:条件满足返回本身,否则 null
val even = 4.takeIf { it % 2 == 0 } // 4
val odd = 4.takeIf { it % 2 != 0 } // null
// takeUnless:条件不满足返回本身
val odd2 = 4.takeUnless { it % 2 == 0 } // null
val even2 = 4.takeUnless { it % 2 != 0 } // 4
repeat / TODO
kotlin
// repeat:重复执行代码块
repeat(3) { index ->
println("Round ${index + 1}")
}
// 输出:Round 1, Round 2, Round 3
repeat(3) { println("Hello") } // 重复 3 次
// withIndex 用于带索引遍历
for ((index, value) in listOf("a", "b", "c").withIndex()) {
println("Index $index: $value")
}
// TODO:标记未实现代码,调用时抛 NotImplementedError
fun unimplemented() {
TODO("Implement this method")
}
fun partiallyImplemented() {
// ...
TODO("Handle edge case: user not found")
}
常用扩展函数
kotlin
// 字符串
" whitespace ".trim() // "whitespace"
"hello".capitalize() // "Hello"
"HELLO".lowercase() // "hello"
"hello".repeat(3) // "hellohellohello"
"ab cd ef".split(" ") // [ab, cd, ef]
"abc".padStart(5, '*') // "**abc"
"abc".padEnd(5, '*') // "abc**"
"abc".removePrefix("ab") // "c"
"abc".removeSuffix("bc") // "a"
// 集合
listOf(1, 2, 3).isEmpty() // false
listOf(1, 2, 3).isNotEmpty() // true
listOf(1, 2, 3).first() // 1
listOf(1, 2, 3).last() // 3
listOf(1, 2, 3).single() // 元素唯一时返回
listOf(1, 2, 2, 3).distinct() // [1, 2, 3]
listOf(1, 2, 3).random() // 随机
listOf(1, 2, 3).shuffled() // 打乱
// 范围
val range = 1..10
range.first // 1
range.last // 10
range.step // 1
(0..10).toList() // [0, 1, 2, ..., 10]
(0 until 10).toList() // [0, 1, 2, ..., 9]
(10 downTo 1).toList() // [10, 9, 8, ..., 1]
// 数值
42.toLong()
3.14.toInt()
Int.MAX_VALUE
Long.MIN_VALUE
42.toString(16) // "2a"(十六进制)
inline 系列函数
kotlin
// use:自动关闭资源(类似 Java try-with-resources)
File("test.txt").inputStream().use { stream ->
val content = stream.bufferedReader().readText()
}
// 注意:标准库 inputStream 返回的是 java.io.InputStream
// 需要自行 close,use 帮你处理
// readText 是 Kotlin 1.5+ 内置
val text = File("test.txt").readText()
// readLines 读取为列表
val lines = File("test.txt").readLines()
// withAutoCloseable(自定义自动关闭)
inline fun <T : AutoCloseable, R> T.withAutoCloseable(block: T.() -> R): R {
try {
return block()
} finally {
close()
}
}
4. 内置扩展
checkNull 与冒泡
kotlin
// ?: 与 let 组合
val length = nullableString?.length ?: 0
// !! 与 let(双重保障)
val len = nullableString!!.let { it.length } // 已知非空
// ?.let?. Elvis 三连
val result = user?.address?.let { addr ->
addr.city?.let { city ->
validateCity(city)
}
} ?: "Unknown"
// also + let 组合
val processed = data
.also { println("Input: $it") }
.let { transform(it) }
?.also { println("Output: $it") }
条件执行
kotlin
// takeIf + ?: 实现 filter 的部分功能
val filtered = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
.asSequence()
.map { it * it }
.filter { it > 10 }
.takeIf { it.any() }
?: emptyList()
// 条件执行
buildString {
appendLine("Header")
if (condition) {
appendLine("Content")
}
}
// 枚举 + when
enum class Mode { READ, WRITE }
val result = when (Mode.READ) {
Mode.READ -> "Reading"
Mode.WRITE -> "Writing"
}
// also 作为日志
listOf(1, 2, 3)
.also { println("Before: $it") }
.map { it * 2 }
.also { println("After: $it") }
初始化相关
kotlin
// 懒初始化
val heavyObject: HeavyClass by lazy {
HeavyClass().also { println("Initialized") }
}
// lateinit 检测
class MyClass {
lateinit var data: Data
fun initialize() {
data = loadData()
}
fun useData() {
if (::data.isInitialized) {
process(data)
}
}
}
// 委托初始化
var value by Delegates.notNull<Int>()
// 延迟初始化值
class Service {
private var _config: Config? = null
val config: Config
get() = _config ?: loadConfig().also { _config = it }
}
线程与并发相关
kotlin
import kotlin.concurrent.thread
// thread:创建线程(Java Thread 的简化版)
thread {
println("Running in background")
}
thread(start = true) {
println("With explicit start")
}
thread(start = true, isDaemon = false) {
println("Non-daemon thread")
}
// synchronized(替代 Java synchronized)
val lock = Any()
synchronized(lock) {
sharedResource += 1
}
// measureTimeMillis / measureNanoTime
import kotlin.system.measureTimeMillis
val time = measureTimeMillis {
Thread.sleep(100)
}
println("Elapsed: $time ms")
// 配合 use 测量资源操作时间
measureTimeMillis {
File("test.txt").inputStream().use { stream ->
stream.readBytes()
}
}
常用内联函数
kotlin
// TODO:标记未实现,返回 Nothing
fun notImplemented(): Nothing = TODO("Implementation required")
// requireNotNull:判空检查
fun processName(name: String?) {
val nonNull = requireNotNull(name) { "Name required" }
println(nonNull)
}
// error:抛异常并附消息
fun parseCommand(cmd: String): Command {
return when (cmd) {
"run" -> RunCommand
else -> error("Unknown command: $cmd")
}
}
// checkNotNull:状态检查
fun ensureInitialized(): Unit {
checkNotNull(initialized) { "Call init() first" }
}
// 组合示例:构建器模式
class Builder {
private var name: String = ""
private var value: Int = 0
fun build(): Product = Product(name, value)
}
inline fun build(block: Builder.() -> Unit): Builder {
return Builder().apply(block)
}
val product = build {
name = "Product"
value = 42
}
5. 综合实战示例
安全的类型转换
kotlin
// safeAsOrNull:安全的类型转换
fun safeAsOrNull(value: Any?, target: KClass<*>): Any? {
return value?.takeIf { target.isInstance(it) }
}
// 配合 when 使用
when (val data = parseInput(raw)) {
is String -> processString(data)
is Int -> processInt(data)
is List<*> -> processList(data)
null -> handleNull()
else -> handleUnknown(data)
}
// resultMap:转换失败返回 null
inline fun <T, R> T.resultMap(transform: (T) -> R): R? {
return runCatching { transform(this) }.getOrNull()
}
资源管理
kotlin
// use 模式:自动关闭资源
inline fun <T : Closeable, R> T.useAuto(block: T.() -> R): R {
try {
return block()
} finally {
close()
}
}
// 数据库连接
fun queryDatabase(sql: String): List<Map<String, Any>> {
return connection.useAuto { conn ->
val statement = conn.createStatement()
val resultSet = statement.executeQuery(sql)
resultSet.useAuto {
val rows = mutableListOf<Map<String, Any>>()
while (it.next()) {
val row = mutableMapOf<String, Any>()
for (i in 1..it.metaData.columnCount) {
row[it.metaData.getColumnName(i)] = it.getObject(i)
}
rows.add(row)
}
rows
}
}
}
完整错误处理策略
kotlin
// 策略一:Result 类型
sealed class ApiResult<out T> {
data class Success<T>(val data: T) : ApiResult<T>()
data class Error(val code: Int, val message: String) : ApiResult<Nothing>()
object Loading : ApiResult<Nothing>()
}
inline fun <T> runApi(block: () -> T): ApiResult<T> {
return runCatching(block)
.map { ApiResult.Success(it) }
.exceptionOrNull()
?.let { ApiResult.Error(-1, it.message ?: "Unknown error") }
?: ApiResult.Success(Unit as T)
}
// 策略二:Boolean + out parameter(Retrofit 旧风格)
fun parseResponse(json: String, result: Consumer<User>) {
// result 被填充,函数返回 true/false
}
// 策略三:Nullable(简单场景)
fun parseUser(json: String): User? {
return runCatching { gson.fromJson(json, User::class.java) }
.getOrNull()
}
// 策略四:runCatching 链式处理
val user: User = runCatching { api.getUser(id) }
.onSuccess { println("Loaded: ${it.name}") }
.onFailure { println("Failed: ${it.message}") }
.getOrElse { cachedUser(id) }
快速对照表
| 工具 | 用途 | 失败时 |
|---|---|---|
require |
前置条件检查 | IllegalArgumentException |
check |
状态检查 | IllegalStateException |
assert |
调试断言 | AssertionError(-ea) |
requireNotNull |
空值检查 | IllegalArgumentException |
checkNotNull |
空状态检查 | IllegalStateException |
TODO |
标记未实现 | NotImplementedError |
error |
抛异常 | 任意异常 |
runCatching |
捕获异常为 Result | 返回 Result |
takeIf |
条件满足返回本身 | null |
takeUnless |
条件不满足返回本身 | null |
use |
自动关闭资源 | 关闭资源后传递异常 |
记忆口诀:
- require = 前置条件 = 需要参数正确
- check = 状态检查 = 需要状态正确
- requireNotNull / checkNotNull = 空值检查
- runCatching = 捕获一切异常 = 返回 Result
- takeIf / takeUnless = 条件过滤 = 返回自身或 null
- use = 资源管理 = 自动关闭 = try-with-resources