Kotlin基础知识点 #122:Lambda表达式详解
·
Kotlin基础知识点 #122:Lambda表达式详解
难度:⭐⭐
🎯 问题
在Android开发中编写点击监听器、线程回调时,Java的匿名内部类代码冗长。Kotlin的Lambda表达式如何简化这些场景?有哪些语法糖和最佳实践?
💡 核心概念
Lambda表达式是匿名函数的简洁表示法,可以作为值传递。Kotlin的Lambda表达式比Java 8更强大,支持多种语法糖和智能类型推断。
Lambda语法结构
{ 参数列表 -> 函数体 }
代码示例
示例1:Lambda基础语法
// 完整语法
val sum: (Int, Int) -> Int = { a: Int, b: Int -> a + b }
// 类型推断:省略参数类型
val sum2: (Int, Int) -> Int = { a, b -> a + b }
// 类型推断:省略变量类型
val sum3 = { a: Int, b: Int -> a + b }
// 单个参数:使用it
val double: (Int) -> Int = { it * 2 }
// 无参数
val greet: () -> String = { "Hello, Kotlin!" }
// 多行Lambda:最后一行作为返回值
val complexCalc = { a: Int, b: Int ->
val temp = a + b
val result = temp * 2
result // 返回值
}
// Android场景:按钮点击
button.setOnClickListener { view ->
Toast.makeText(this, "点击了按钮", Toast.LENGTH_SHORT).show()
}
// 简化:不使用参数
button.setOnClickListener {
Toast.makeText(this, "点击了按钮", Toast.LENGTH_SHORT).show()
}
示例2:Lambda作为最后参数(尾随Lambda)
// 标准库函数
fun <T> List<T>.customFilter(predicate: (T) -> Boolean): List<T> {
val result = mutableListOf<T>()
for (item in this) {
if (predicate(item)) result.add(item)
}
return result
}
// 使用方式对比
fun testTrailingLambda() {
val numbers = listOf(1, 2, 3, 4, 5)
// ❌ 不推荐:Lambda在括号内
val evens1 = numbers.customFilter({ it % 2 == 0 })
// ✅ 推荐:尾随Lambda(括号外)
val evens2 = numbers.customFilter { it % 2 == 0 }
// 唯一参数是Lambda时,括号可省略
val doubled = numbers.map { it * 2 }
}
// Android场景:协程启动
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 尾随Lambda:简洁的异步代码
lifecycleScope.launch {
val data = fetchDataFromNetwork()
updateUI(data)
}
}
private suspend fun fetchDataFromNetwork(): String {
delay(1000)
return "Network Data"
}
private fun updateUI(data: String) {
// 更新UI
}
}
示例3:Lambda中的return和标签
// 问题:Lambda中的return行为
fun testReturn() {
val numbers = listOf(1, 2, 3, 4, 5)
// ❌ 错误:会导致整个testReturn函数返回
// numbers.forEach {
// if (it == 3) return // 非局部返回
// println(it)
// }
// ✅ 方式1:使用标签返回(局部返回)
numbers.forEach label@{
if (it == 3) return@label // 只退出当前Lambda
println(it)
}
// 输出: 1, 2, 4, 5
// ✅ 方式2:使用隐式标签(函数名)
numbers.forEach {
if (it == 3) return@forEach
println(it)
}
// ✅ 方式3:使用匿名函数(不是Lambda)
numbers.forEach(fun(value: Int) {
if (value == 3) return // 只退出匿名函数
println(value)
})
println("函数继续执行")
}
// Android场景:列表过滤处理
class DeviceListFragment : Fragment() {
private fun processDevices(devices: List<Device>) {
devices.forEach { device ->
// 跳过离线设备
if (!device.isOnline) return@forEach
// 处理在线设备
updateDeviceStatus(device)
}
// 所有设备处理完成
showCompleteMessage()
}
private fun updateDeviceStatus(device: Device) {}
private fun showCompleteMessage() {}
}
data class Device(val id: String, val isOnline: Boolean)
示例4:Lambda接收者(带接收者的Lambda)
// 普通Lambda vs 带接收者的Lambda
fun buildString1(action: (StringBuilder) -> Unit): String {
val sb = StringBuilder()
action(sb) // 传递sb作为参数
return sb.toString()
}
fun buildString2(action: StringBuilder.() -> Unit): String {
val sb = StringBuilder()
sb.action() // sb作为接收者
return sb.toString()
}
// 使用对比
fun testReceiverLambda() {
// 普通Lambda:需要显式使用参数
val result1 = buildString1 { sb ->
sb.append("Hello")
sb.append(" ")
sb.append("World")
}
// 带接收者的Lambda:直接调用StringBuilder方法
val result2 = buildString2 {
append("Hello") // this.append("Hello")
append(" ")
append("World")
}
println(result1) // "Hello World"
println(result2) // "Hello World"
}
// Android场景:DSL风格的View构建
class ViewBuilder {
fun createLayout(init: LinearLayout.() -> Unit): LinearLayout {
val layout = LinearLayout(context)
layout.init()
return layout
}
}
// 使用示例
fun buildUI() {
val layout = ViewBuilder().createLayout {
orientation = LinearLayout.VERTICAL
addView(TextView(context).apply {
text = "标题"
textSize = 18f
})
addView(Button(context).apply {
text = "提交"
})
}
}
private val context: Context get() = TODO()
示例5:Lambda捕获变量
// Lambda可以访问外部变量(闭包)
fun testClosure() {
var sum = 0
val numbers = listOf(1, 2, 3, 4, 5)
// Lambda捕获并修改外部变量
numbers.forEach {
sum += it
}
println("Total: $sum") // 15
}
// Android场景:计数器
class CounterActivity : AppCompatActivity() {
private var clickCount = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val button = findViewById<Button>(R.id.btnClick)
val textView = findViewById<TextView>(R.id.tvCount)
// Lambda捕获并修改Activity成员变量
button.setOnClickListener {
clickCount++
textView.text = "点击次数: $clickCount"
}
}
}
// 注意:Lambda捕获可能导致内存泄漏
class LeakExample {
fun setupCallback() {
val heavyObject = HeavyObject()
// ❌ 危险:Lambda持有heavyObject引用
GlobalScope.launch {
delay(10000)
heavyObject.doSomething() // 10秒内heavyObject无法被回收
}
// ✅ 改进:使用弱引用
val weakRef = WeakReference(heavyObject)
GlobalScope.launch {
delay(10000)
weakRef.get()?.doSomething()
}
}
}
class HeavyObject {
fun doSomething() {}
}
⚡ 关键要点
1. Lambda语法糖总结
// 完整形式
list.filter({ item -> item > 0 })
// 尾随Lambda
list.filter() { item -> item > 0 }
// 省略空括号
list.filter { item -> item > 0 }
// 使用it
list.filter { it > 0 }
2. 何时使用it
// ✅ 推荐:简单表达式
list.filter { it > 0 }
list.map { it * 2 }
// ❌ 不推荐:复杂逻辑,使用命名参数
list.filter { user ->
user.age > 18 &&
user.isActive &&
user.roles.contains("admin")
}
3. Lambda性能考虑
// Lambda会创建对象,有性能开销
// 对于性能敏感的代码,考虑使用inline函数
inline fun <T> List<T>.fastFilter(predicate: (T) -> Boolean): List<T> {
val result = mutableListOf<T>()
for (item in this) {
if (predicate(item)) result.add(item)
}
return result
}
4. Lambda vs 匿名函数
// Lambda:不能指定返回类型
val lambda = { x: Int -> x * 2 }
// 匿名函数:可以指定返回类型
val anonymousFunc = fun(x: Int): Int {
return x * 2
}
// 匿名函数中的return行为更直观
list.forEach(fun(value) {
if (value == 0) return // 只退出当前函数
println(value)
})
🔗 相关知识点
- #121 高阶函数 - Lambda的使用场景
- #124 inline内联函数 - 优化Lambda性能
- #114 apply作用域函数 - 带接收者的Lambda应用
- #132 let、run、apply组合使用 - Lambda的实战技巧
总结
Lambda表达式是Kotlin最常用的特性之一,极大简化了Android开发中的回调、监听器和函数式操作。掌握Lambda的各种语法糖和最佳实践,能写出更简洁、更易读的代码。注意避免闭包导致的内存泄漏问题。
更多推荐
所有评论(0)