Kotlin 从入门到进阶 之异常与标准库(八)

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
相关推荐
xyq20241 小时前
Swift 类
开发语言
Brilliantwxx1 小时前
【C++】认识 list(初步认识+模拟实现)
开发语言·数据结构·c++·笔记·算法·list
曹牧1 小时前
Java:数据载体
java·开发语言
我这一生如履薄冰~1 小时前
flutter开发适配底部导航条样式
android·flutter
赏金术士1 小时前
Kotlin 从入门到进阶 之基础语法模块(一)
开发语言·微信·kotlin
格林威1 小时前
Baumer工业相机堡盟相机Chunk功能全解析:如何在图像中嵌入时间戳、编码器值等元数据?
开发语言·人工智能·数码相机·机器学习·计算机视觉·视觉检测·机器视觉
神探小白牙1 小时前
echarts环形图自定义
android·前端·echarts
南宫萧幕1 小时前
锂电池二阶 RC 模型仿真实战:从理论解析到 Simulink 闭环搭建全流程
开发语言·人工智能·算法·机器学习