2-2-17 快速掌握Kotlin-标准函数与泛型扩展函数

65 阅读3分钟

Kotlin 语言标准函数与泛型扩展函数

在 Kotlin 中,标准库提供了一系列强大的标准函数(scope functions),它们都是泛型扩展函数。这些函数可以让你在对象的上下文中执行代码块,从而简化代码。

1. Kotlin 标准函数概述

Kotlin 标准库提供了 5 个主要的标准函数,它们都是泛型扩展函数:

函数接收者返回值使用场景
letitLambda 结果非空检查、转换
runthisLambda 结果对象配置、计算值
withthisLambda 结果对象操作、不需要结果
applythis接收者对象对象初始化
alsoit接收者对象附加操作、日志记录

2. let 函数

基本用法

// let 的简化签名
public inline fun <T, R> T.let(block: (T) -> R): R

// 使用示例
val name: String? = "Kotlin"

// 1. 安全调用
val length = name?.let { 
    println("字符串: $it")
    it.length  // 返回值
} ?: 0  // 如果 name 为 null,返回 0

// 2. 链式调用
val result = listOf(1, 2, 3)
    .let { it.filter { num -> num > 1 } }
    .let { it.map { num -> num * 2 } }
    .let { it.sum() }

println(result)  // 10 (2+3)*2

实际应用场景

// 1. 空安全转换
val number: String? = "123"
val parsedNumber = number?.let { it.toIntOrNull() }

// 2. 作用域限制
fun processUser(user: User?) {
    user?.let { // it: User
        // 这里可以安全地使用 user 对象
        println("处理用户: ${it.name}")
        saveToDatabase(it)
    }
}

// 3. 避免变量名冲突
val outerVariable = "外部变量"
"新值".let { innerVariable ->
    println(outerVariable)  // 外部变量
    println(innerVariable)  // 新值
}

3. run 函数

基本用法

// run 的两种形式
public inline fun <T, R> T.run(block: T.() -> R): R
public inline fun <R> run(block: () -> R): R

// 使用示例
val result1 = "Kotlin".run {
    println("字符串长度: $length")
    length * 2  // 返回值
}

// 不带接收者的 run
val result2 = run {
    val x = 5
    val y = 10
    x + y  // 返回值
}

// 对象配置
val service = DatabaseService().run {
    host = "localhost"
    port = 5432
    connect()
    this  // 返回配置好的对象
}

实际应用场景

// 1. 对象初始化
val connection = DatabaseConnection().run {
    url = "jdbc:mysql://localhost:3306/test"
    username = "root"
    password = "password"
    connect()
    this
}

// 2. 计算复杂表达式
val formattedDate = Date().run {
    SimpleDateFormat("yyyy-MM-dd").format(this)
}

// 3. 链式调用
val configuration = Configuration().run {
    loadFromFile("config.json")
    validate()
    this
}

4. with 函数

基本用法

// with 的签名
public inline fun <T, R> with(receiver: T, block: T.() -> R): R

// 使用示例
val builder = StringBuilder()

val result = with(builder) {
    // this 指向 builder
    append("Hello")
    append(" ")
    append("Kotlin")
    toString()  // 返回值
}

println(result)  // Hello Kotlin

// 对象操作
class Person(var name: String, var age: Int)

val person = Person("Alice", 30)
with(person) {
    println("姓名: $name, 年龄: $age")
    age += 1  // 修改年龄
}

实际应用场景

// 1. 批量操作对象属性
val dialog = CustomDialog(context)
with(dialog) {
    title = "提示"
    message = "确认删除吗?"
    positiveButtonText = "确定"
    negativeButtonText = "取消"
    show()
}

// 2. 复杂计算
val rectangle = Rectangle(10.0, 5.0)
val area = with(rectangle) {
    width * height
}

// 3. 创建只读块
fun printUserInfo(user: User) {
    with(user) {
        println("ID: $id")
        println("名称: $name")
        println("邮箱: $email")
    }
}

5. apply 函数

基本用法

// apply 的签名
public inline fun <T> T.apply(block: T.() -> Unit): T

// 使用示例
val list = mutableListOf<Int>().apply {
    // this 指向 list
    add(1)
    add(2)
    add(3)
    // 隐式返回 this
}

println(list)  // [1, 2, 3]

// 对象初始化
val user = User().apply {
    name = "John"
    email = "john@example.com"
    age = 25
}

实际应用场景

// 1. Builder 模式
val intent = Intent().apply {
    action = "com.example.ACTION"
    putExtra("key", "value")
    flags = Intent.FLAG_ACTIVITY_NEW_TASK
}

// 2. 配置 UI 组件
val textView = TextView(context).apply {
    text = "Hello World"
    textSize = 16f
    setTextColor(Color.BLACK)
    gravity = Gravity.CENTER
}

// 3. 集合初始化
val configuration = mapOf<String, Any>(
    "timeout" to 5000,
    "retries" to 3
).apply {
    // 可以添加更多配置
}

6. also 函数

基本用法

// also 的签名
public inline fun <T> T.also(block: (T) -> Unit): T

// 使用示例
val numbers = mutableListOf(1, 2, 3)

val result = numbers.also { list ->
    // it 或显式参数名
    println("操作前: $list")
    list.add(4)
}.also { list ->
    println("操作后: $list")
    list.add(5)
}

println(result)  // [1, 2, 3, 4, 5]

实际应用场景

// 1. 调试日志
val result = complexCalculation()
    .also { println("计算结果: $it") }
    .also { logToFile(it) }

// 2. 副作用操作
File("data.txt").also { file ->
    if (!file.exists()) {
        file.createNewFile()
    }
}.readText()

// 3. 验证和修改
val user = getUserFromApi()
    .also { validateUser(it) }
    .also { it.lastLogin = Date() }

7. 标准函数的比较与选择

决策流程图

开始
  ↓
需要返回值吗?
  ├── 否 → 使用 also 或 apply
  │       ├── 需要返回接收者本身? → apply (使用 this)
  │       └── 需要额外操作/日志? → also (使用 it)
  │
  └── 是 → 使用 let、run 或 with
          ├── 接收者可能为 null? → let (使用 it)
          ├── 需要 this 上下文? → run 或 with
          │   ├── 接收者是参数? → with
          │   └── 接收者是扩展? → run
          └── 简单的转换? → let

对比示例

data class Person(var name: String, var age: Int)

// 场景:创建并初始化 Person
val person1 = Person("Alice", 30).apply {
    // 适合初始化,返回 this
    age += 1
}

val person2 = Person("Bob", 25).also {
    // 适合副作用操作,返回 this
    println("创建了: ${it.name}")
}

val nameLength = person1.name.let {
    // 适合转换,返回 lambda 结果
    it.length
}

val description = person1.run {
    // 适合计算并返回结果
    "$name 今年 $age 岁"
}

with(person1) {
    // 适合对已有对象进行操作
    println("修改前: $name")
    name = "Alice Smith"
}

8. 创建自定义标准函数

基于现有标准函数的扩展

// 1. 带条件执行的 let
inline fun <T> T?.letIf(
    condition: Boolean,
    block: (T) -> Unit
): T? {
    if (condition && this != null) {
        block(this)
    }
    return this
}

// 使用
val value: String? = "test"
value.letIf(shouldLog) {
    println("值: $it")
}

// 2. 重试机制的 run
inline fun <T> T.runWithRetry(
    maxRetries: Int = 3,
    block: T.() -> Unit
) {
    var retries = 0
    while (retries < maxRetries) {
        try {
            block()
            break
        } catch (e: Exception) {
            retries++
            if (retries == maxRetries) throw e
        }
    }
}

// 使用
databaseConnection.runWithRetry {
    executeQuery("SELECT * FROM users")
}

完全自定义的标准函数

// 带超时控制的 run
inline fun <T, R> T.runWithTimeout(
    timeoutMillis: Long,
    block: T.() -> R
): R? {
    val future = CompletableFuture<R?>()
    
    Thread {
        try {
            future.complete(block())
        } catch (e: Exception) {
            future.completeExceptionally(e)
        }
    }.start()
    
    return try {
        future.get(timeoutMillis, TimeUnit.MILLISECONDS)
    } catch (e: TimeoutException) {
        null
    }
}

// 使用
val result = heavyComputation.runWithTimeout(5000) {
    compute()
}

9. 实际项目中的组合使用

Android 开发示例

// 1. Activity 启动
fun startDetailActivity(item: Item) {
    Intent(this, DetailActivity::class.java).apply {
        putExtra("item_id", item.id)
        putExtra("item_name", item.name)
    }.also { intent ->
        if (intent.resolveActivity(packageManager) != null) {
            startActivity(intent)
        } else {
            showToast("无法启动详情页")
        }
    }
}

// 2. View 初始化
val recyclerView = RecyclerView(context).apply {
    layoutManager = LinearLayoutManager(context)
    adapter = createAdapter()
    addItemDecoration(DividerItemDecoration(context, VERTICAL))
}.also { view ->
    // 添加到父布局
    parentLayout.addView(view)
    // 配置其他属性
    view.layoutParams = ViewGroup.LayoutParams(
        ViewGroup.LayoutParams.MATCH_PARENT,
        ViewGroup.LayoutParams.WRAP_CONTENT
    )
}

Web 开发示例

// 1. HTTP 响应处理
fun handleResponse(response: HttpResponse) = response.run {
    if (statusCode == 200) {
        body?.let { body ->
            parseJson<User>(body)
        }?.apply { user ->
            saveToDatabase(user)
        }?.also { user ->
            logSuccess(user)
        }
    } else {
        throw HttpException("请求失败: $statusCode")
    }
}

// 2. 配置对象构建
val config = ServerConfig().apply {
    host = "localhost"
    port = 8080
    sslEnabled = true
    sslConfig = SslConfig().apply {
        certificatePath = "/path/to/cert"
        keyPath = "/path/to/key"
    }
}.also { config ->
    validateConfig(config)
    logConfig(config)
}

10. 性能考虑与最佳实践

性能差异

// 1. 内联函数
// 所有标准函数都是 inline 的,没有额外函数调用开销
val result = value.let { it * 2 }
// 编译后大致等同于:
// val result = value * 2

// 2. 链式调用
// 避免过深的链式调用
val processed = data
    .let { transform1(it) }  // 1层
    .let { transform2(it) }  // 2层
    .let { transform3(it) }  // 3层
// 如果层级太深,考虑重构

// 3. 空检查优化
// 不好的写法:多次空检查
value?.let { 
    // 操作1
}
value?.also {
    // 操作2
}

// 好的写法:合并空检查
value?.let { v ->
    v.操作1()
    v.操作2()
}

最佳实践

// 1. 保持简洁
// 不好
val result = value.let { it -> 
    val temp = it * 2
    println(temp)
    temp + 1
}

// 好
val result = value.let { it * 2 + 1 }

// 2. 明确使用场景
// 初始化对象 → apply
val view = TextView().apply { text = "Hello" }

// 转换结果 → let
val length = name?.let { it.length } ?: 0

// 副作用 → also
file.writeText(content).also { logWrite(it) }

// 3. 避免滥用
// 不要为了使用而使用
val name = "Alice"
// 不好
name.let { println(it) }

// 好
println(name)

11. 标准函数的类型安全扩展

// 1. 安全类型转换
inline fun <reified T> Any?.safeLet(block: (T) -> Unit) {
    if (this is T) {
        block(this)
    }
}

// 使用
val anyValue: Any = "Hello"
anyValue.safeLet<String> { 
    println("字符串长度: ${it.length}") 
}

// 2. 条件执行扩展
fun <T> T.takeIfNotNull(block: (T) -> Unit): T? {
    return this?.also(block)
}

// 3. 异步支持
suspend fun <T> T.letAsync(
    block: suspend (T) -> Unit
) {
    block(this)
}

// 使用
user.letAsync { 
    saveToDatabaseAsync(it) 
}

总结

Kotlin 的标准函数是非常强大的工具,它们:

  1. 简化代码:减少临时变量,使代码更简洁
  2. 提高可读性:通过链式调用表达清晰的操作流程
  3. 增强安全性:支持空安全操作
  4. 提供灵活性:不同的函数适用于不同的场景

选择哪个标准函数取决于:

  • 是否需要返回值
  • 需要返回什么(接收者本身还是 lambda 结果)
  • 是否需要 thisit 作为上下文
  • 是否需要处理 null 值

合理使用标准函数可以让你的 Kotlin 代码更加优雅和高效。