Kotlin 接收者关键字
最后修改于 2025 年 4 月 19 日
Kotlin 的接收者概念支持强大的 DSL 创建和扩展函数。接收者关键字允许在对象的上下文中调用函数。本教程通过实际示例深入探讨了接收者。
基本定义
Kotlin 中的接收者是调用函数或属性的对象。this
关键字在其范围内引用接收者。带有接收者的函数字面量允许在隐式对象上调用方法。
扩展函数
扩展函数是接收者的最简单形式。它们无需继承即可向现有类添加功能。接收者是正在扩展的对象。
package com.zetcode fun String.addExclamation(): String { return "$this!" } fun main() { val greeting = "Hello" println(greeting.addExclamation()) // Output: Hello! }
这里,String
是接收者类型。在函数内部,this
指的是字符串实例。我们可以像在任何 String 上一样调用它。
带有接收者的函数字面量
Lambda 表达式可以有接收者,从而实现类似 DSL 的语法。接收者在 lambda 内部作为 this
可用。
package com.zetcode class Html { fun body() = println("Creating body") fun div() = println("Adding div") } fun html(init: Html.() -> Unit): Html { val html = Html() html.init() return html } fun main() { html { body() div() } }
html
函数接受一个带有 Html 接收者的 lambda。在 lambda 内部,我们可以直接调用 Html 方法。这种模式在 DSL 构造中很常见。
作用域函数
Kotlin 的标准作用域函数(let
、run
等)广泛使用接收者。它们提供了不同的方式来访问接收者对象。
package com.zetcode data class Person(var name: String, var age: Int) fun main() { val person = Person("Alice", 25) person.run { println("Name: $name, Age: $age") // this is implicit } person.let { println("Name: ${it.name}, Age: ${it.age}") // explicit it } }
run
使用接收者作为 this
,而 let
使用 it
。两者都提供了以不同风格访问对象的方式。根据可读性需求进行选择。
DSL 构建
接收者通过嵌套上下文实现干净的 DSL 语法。每个嵌套块都有自己的接收者,从而实现分层结构。
package com.zetcode class Table { fun tr(init: Tr.() -> Unit) { Tr().init() } } class Tr { fun td(content: String) { println("<td>$content</td>") } } fun table(init: Table.() -> Unit): Table { val table = Table() table.init() return table } fun main() { table { tr { td("Data 1") td("Data 2") } } }
此 HTML DSL 示例显示了嵌套接收者。table
接收者启用 tr
调用,而 tr
接收者启用 td
。每个块都在其特定上下文中运行。
高阶函数中的接收者
高阶函数可以将带有接收者的函数作为参数。这允许灵活的行为注入,同时保持上下文。
package com.zetcode class Calculator { var result = 0 fun add(value: Int) { result += value } fun subtract(value: Int) { result -= value } } fun calculate(operations: Calculator.() -> Unit): Int { val calculator = Calculator() calculator.operations() return calculator.result } fun main() { val result = calculate { add(5) subtract(3) add(10) } println(result) // Output: 12 }
calculate
函数接受一个带有 Calculator 接收者的 lambda。在 lambda 内部,我们可以直接调用 Calculator 方法。该函数在所有操作完成后返回最终结果。
多个接收者
Kotlin 允许使用嵌套函数类型指定多个接收者。这支持具有多个上下文的复杂 DSL。
package com.zetcode class Database { fun query(sql: String) = println("Executing: $sql") } class Logger { fun log(message: String) = println("LOG: $message") } fun withDatabaseAndLogger(action: Database.(Logger) -> Unit) { val db = Database() val logger = Logger() db.action(logger) } fun main() { withDatabaseAndLogger { logger -> logger.log("Starting query") query("SELECT * FROM users") logger.log("Query completed") } }
此示例显示了一个带有两个接收者的函数:Database 作为主接收者,Logger 作为参数。lambda 可以访问这两个上下文,从而实现组件之间的协调操作。
泛型函数中的接收者
泛型函数可以使用接收者对各种类型执行类型安全操作,同时保持上下文。
package com.zetcode fun <T> T.applyIf(condition: Boolean, block: T.() -> T): T { return if (condition) block() else this } fun main() { val number = 10 val result = number.applyIf(number > 5) { this * 2 } println(result) // Output: 20 val text = "Hello" val modified = text.applyIf(text.length > 3) { uppercase() } println(modified) // Output: HELLO }
泛型 applyIf
函数适用于任何类型 T。接收者块可以执行特定于类型的操作。条件决定是否应用该块。
接收者的最佳实践
- 明确的上下文: 确保接收者的目的从函数名称或 DSL 结构中显而易见。
- 限制范围: 使接收者函数专注于与接收者类型相关的操作。
- 文档: 在使用复杂的接收者层次结构时,清楚地记录预期的行为。
- 类型安全: 将泛型与接收者结合使用,以在灵活的 API 中保持类型安全。
- 可读性: 在嵌套可能导致混淆时,优先使用显式接收者 (
this
)。
来源
本教程深入探讨了 Kotlin 的接收者概念,展示了从扩展函数到 DSL 构造的各种应用。接收者可以实现强大且具有上下文感知能力的代码,同时保持可读性和类型安全。
作者
列出 所有 Kotlin 教程。