Kotlin 语言标准函数与泛型扩展函数
在 Kotlin 中,标准库提供了一系列强大的标准函数(scope functions),它们都是泛型扩展函数。这些函数可以让你在对象的上下文中执行代码块,从而简化代码。
1. Kotlin 标准函数概述
Kotlin 标准库提供了 5 个主要的标准函数,它们都是泛型扩展函数:
| 函数 | 接收者 | 返回值 | 使用场景 |
|---|---|---|---|
let | it | Lambda 结果 | 非空检查、转换 |
run | this | Lambda 结果 | 对象配置、计算值 |
with | this | Lambda 结果 | 对象操作、不需要结果 |
apply | this | 接收者对象 | 对象初始化 |
also | it | 接收者对象 | 附加操作、日志记录 |
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 的标准函数是非常强大的工具,它们:
- 简化代码:减少临时变量,使代码更简洁
- 提高可读性:通过链式调用表达清晰的操作流程
- 增强安全性:支持空安全操作
- 提供灵活性:不同的函数适用于不同的场景
选择哪个标准函数取决于:
- 是否需要返回值
- 需要返回什么(接收者本身还是 lambda 结果)
- 是否需要
this或it作为上下文 - 是否需要处理 null 值
合理使用标准函数可以让你的 Kotlin 代码更加优雅和高效。