Kotlin: Kotlin函数与Lambda表达式
最后更新:2026-08-26
函数是 Kotlin 的一等公民——高阶函数 + Lambda + 内联让 Charlie 用函数式风格构建 OrderProcessor 的数据处理流水线,代码量减少 60%。
1. 你将学到
- 函数声明:默认参数、命名参数、表达式体
- 高阶函数:函数作为参数和返回值
- Lambda 语法: 与 简写
- 尾 Lambda 约定:
- 内联函数 :消除 Lambda 运行时开销
2. 一个开发者的真实故事
(1) 痛点:重复的订单处理逻辑
Charlie 的团队中,每个开发者各写一套订单过滤/转换/汇总逻辑,100+ 行重复代码散落在 5 个服务中。修改一个过滤条件要改 5 处。
(2) 高阶函数消除重复
KOTLIN
// Generic order processor - one function replaces 5 copies
fun `<T>` processOrders(
orders: List`<Order>`,
filter: (Order) -> Boolean,
transform: (Order) -> T
): List`<T>` = orders.filter(filter).map(transform)
// Usage: each call site is 2 lines instead of 20
val highValueIds = processOrders(orders, { it.total > 1_000 }, { it.id })
val vipCustomers = processOrders(orders, { it.total > 10_000 }, { it.customer })
高阶函数将"变化的部分"参数化,"不变的部分"只写一次。
3. 函数声明
(1) 基础函数
KOTLIN
// Block body
fun calculateTax(total: Double, rate: Double = 0.08): Double {
return total * rate
}
// Expression body
fun calculateTax(total: Double, rate: Double = 0.08) = total * rate
// Unit return (void)
fun logOrder(order: Order) {
println("Processing ${order.id}")
}
(2) 默认参数与命名参数
KOTLIN
// Default parameters - eliminates method overloading
fun createOrder(
id: String,
total: Double,
status: String = "PENDING",
currency: String = "USD",
taxRate: Double = 0.08
): Order = Order(id, total, status, currency)
// Named arguments - call any parameter by name
val order1 = createOrder("ORD-001", 299.99)
val order2 = createOrder("ORD-002", 1_500.00, taxRate = 0.10)
val order3 = createOrder("ORD-003", 45.50, currency = "EUR", status = "CONFIRMED")
(3) 默认参数 vs Java 方法重载
| 维度 | Java 重载 | Kotlin 默认参数 |
|---|---|---|
| 代码量 | N 个方法 | 1 个方法 |
| 可维护性 | 改一个参数改 N 处 | 改一处 |
| 调用灵活性 | 按参数顺序 | 命名参数任意组合 |
| 互操作 | Java 直接调用 | 需 |
4. 高阶函数
高阶函数是接收函数作为参数或返回函数的函数——Kotlin 函数式编程的基石。
(1) 函数类型
KOTLIN
// Function type syntax
val transform: (Order) -> String = { order -> order.id }
val predicate: (Order) -> Boolean = { it.total > 1_000 }
val aggregator: (Double, Order) -> Double = { acc, order -> acc + order.total }
val supplier: () -> Order = { Order("DEFAULT", 0.0, "PENDING") }
// Function as parameter
fun processOrders(orders: List`<Order>`, transform: (Order) -> String): List`<String>` {
return orders.map(transform)
}
(2) 函数作为返回值
KOTLIN
// Return a function
fun getDiscountStrategy(tier: String): (Double) -> Double = when (tier) {
"VIP" -> { total -> total * 0.80 } // 20% off
"GOLD" -> { total -> total * 0.90 } // 10% off
else -> { total -> total } // No discount
}
val discount = getDiscountStrategy("VIP")
val finalPrice = discount(1_000.00) // 800.0
(3) 高阶函数 + Lambda 调用链
flowchart LR
A[Orders] --> B[filter]
B --> C[map]
C --> D[sortedBy]
D --> E[fold]
E --> F[Result]
5. Lambda 表达式
(1) Lambda 语法
KOTLIN
// Full syntax
val total = orders.map({ order: Order -> order.total })
// Type inference
val total = orders.map({ order -> order.total })
// it keyword for single parameter
val total = orders.map({ it.total })
// Trailing lambda convention
val total = orders.map { it.total }
// Multi-parameter lambda
val result = orders.fold(0.0) { acc, order -> acc + order.total }
(2) Lambda 语法简化层级
| 层级 | 写法 | 说明 |
|---|---|---|
| 完整 | 显式类型 | |
| 简化 1 | 类型推断 | |
| 简化 2 | 单参数用 it | |
| 尾 Lambda | 括号外 Lambda |
(3) 常用 Lambda 模式
KOTLIN
// let: transform and consume
val formatted = order.total.let { "\$$it USD" }
// also: side effect without changing value
val order = createOrder("ORD-001", 299.99).also {
println("Created order: ${it.id}")
}
// apply: configure object
val order = Order("ORD-001", 0.0, "PENDING").apply {
// Can only be used with var properties
}
// run: execute block and return result
val summary = order.run {
"Order $id: \$${total} USD ($status)"
}
6. 尾 Lambda 约定
当函数最后一个参数是 Lambda 时,可以写在括号外面——这是 Kotlin 最重要的语法糖。
(1) 基本用法
KOTLIN
// Definition
fun processOrders(orders: List`<Order>`, callback: (Order) -> Unit) { ... }
// Call: trailing lambda
processOrders(orders) { order ->
println(order.id)
}
// If lambda is the ONLY parameter, omit parentheses
orders.forEach { println(it.id) }
(2) 尾 Lambda 构建 DSL 的基础
KOTLIN
// This is how DSLs work
fun order(id: String, init: OrderBuilder.() -> Unit): Order {
val builder = OrderBuilder(id)
builder.init()
return builder.build()
}
// Usage: trailing lambda makes it read like configuration
val myOrder = order("ORD-001") {
total(299.99)
status("CONFIRMED")
customer("Alice")
}
7. 内联函数(inline)
Lambda 在 JVM 上会编译为匿名类对象,每次调用都创建新实例。 消除这个开销。
(1) inline 基础
KOTLIN
// Without inline: Lambda creates an object every call
fun process(order: Order, block: (Order) -> String): String {
return block(order)
}
// With inline: Lambda code is inlined at call site
inline fun process(order: Order, block: (Order) -> String): String {
return block(order)
}
(2) inline 效果对比
KOTLIN
// After inlining, this:
process(order) { it.id }
// Becomes (conceptually):
run { order.id }
(3) inline vs noinline vs crossinline
| 修饰符 | 效果 | 适用场景 |
|---|---|---|
| Lambda 代码内联到调用处 | 高频调用的 Lambda | |
| 阻止内联(保持为对象) | Lambda 需要被存储/传递 | |
| 内联但禁止非局部 return | Lambda 在其他执行上下文 |
(4) reified 类型参数
KOTLIN
// Normal: type is erased at runtime
fun `<T>` isType(value: Any): Boolean = value is T // ERROR
// reified: inline preserves type info
inline fun <reified T> isType(value: Any): Boolean = value is T // OK
isType`<String>`("Hello") // true
isType`<Int>`(42) // true
isType`<Int>`("42") // false
8. 完整示例:OrderProcessor 函数式流水线
KOTLIN
// ============================================
// OrderProcessor - Functional Pipeline
// Feature: Process orders with higher-order functions
// ============================================
data class Order(val id: String, val total: Double, val status: String, val customer: String)
// Generic pipeline processor
inline fun <T, R> List`<T>`.pipeline(
filter: (T) -> Boolean,
transform: (T) -> R,
crossinline postProcess: (R) -> Unit
): List`<R>` {
return this.filter(filter).map(transform).also { results ->
results.forEach(postProcess)
}
}
// Discount strategy factory
fun getDiscountStrategy(tier: String): (Double) -> Double = when (tier) {
"VIP" -> { total -> total * 0.80 }
"GOLD" -> { total -> total * 0.90 }
"SILVER" -> { total -> total * 0.95 }
else -> { total -> total }
}
fun main() {
val orders = listOf(
Order("ORD-001", 299.99, "CONFIRMED", "Alice"),
Order("ORD-002", 15_000.00, "CONFIRMED", "Bob"),
Order("ORD-003", 2_500.00, "PENDING", "Charlie"),
Order("ORD-004", 45.50, "CANCELLED", "Alice"),
Order("ORD-005", 8_000.00, "CONFIRMED", "Bob")
)
// Pipeline: filter confirmed -> apply VIP discount -> log
val discount = getDiscountStrategy("VIP")
val processed = orders.pipeline(
filter = { it.status == "CONFIRMED" },
transform = { it.copy(total = discount(it.total)) },
postProcess = { println(" Processed: ${it.id} -> \$${it.total} USD") }
)
// Aggregate results
val totalRevenue = processed.sumOf { it.total }
val avgOrder = processed.map { it.total }.average()
println("\nRevenue: \$$totalRevenue USD | Avg: \$${"%.2f".format(avgOrder)} USD")
// Functional composition with let/also/run
val report = orders
.filter { it.status != "CANCELLED" }
.groupBy { it.customer }
.mapValues { (_, list) ->
list.sumOf { it.total }
}
.also { println("\nRevenue by customer:") }
.map { (customer, total) -> "$customer: \$$total USD" }
.joinToString("\n ")
println(" $report")
}
输出:
TEXT
📖 仅展示
Processed: ORD-001 -> $239.992 USD
Processed: ORD-002 -> $12000.0 USD
Processed: ORD-005 -> $6400.0 USD
Revenue: $18639.992 USD | Avg: $6213.330666666667 USD
Revenue by customer:
Alice: $299.99 USD
Bob: $23000.0 USD
Charlie: $2500.0 USD
❓ 常见问题
Q Lambda 和匿名函数有什么区别?
A Lambda 用 隐式参数和 行为不同(Lambda 的 return 是非局部的,匿名函数的 return 是局部的)。大多数场景用 Lambda。
Q 什么时候用 ,什么时候显式命名?
A Lambda 体只有 1-2 行且含义清晰时用 ;超过 2 行或有多个 Lambda 嵌套时显式命名以提高可读性。
Q 所有高阶函数都应该加 inline 吗?
A 不是。inline 适合函数体小且 Lambda 被直接调用的场景。大函数加 inline 会增加代码体积。标准库的高阶函数大多 inline。
Q reified 能用于普通类吗?
A 不能。reified 只能用于 inline 函数的类型参数,因为内联时编译器能在调用处保留类型信息。普通函数的类型参数在运行时被擦除。
Q 尾 Lambda 约定只是语法糖吗?
A 语法糖但非常重要——它是 Kotlin DSL 构建的基础。Gradle Kotlin DSL、Compose UI、Anko 等都依赖这个约定。
Q /// 怎么选择?
A 用于转换值、 用于副作用、 用于配置对象返回自身、 用于执行块返回结果。记住: 返回 this, 返回 Lambda 结果。
📖 小节
- 默认参数 + 命名参数消灭 Java 方法重载,一个函数搞定所有调用组合
- 高阶函数将"变化的部分"参数化,消除重复逻辑
- Lambda 语法从完整到精简: → → 尾 Lambda
- 尾 Lambda 约定是 Kotlin DSL 的语法基础
- 消除 Lambda 对象分配开销, 保留泛型类型信息
- /// 是常用的作用域函数,各有用途
📝 作业
- 基础题(难度⭐):写一个 函数,用命名参数调用。提示:
- 进阶题(难度⭐⭐):用高阶函数实现 ,接收过滤条件和转换函数,返回结果列表。提示:
<T> - 挑战题(难度⭐⭐⭐):实现一个
<Any>函数,从混合类型列表中过滤出指定类型的元素。提示: