Kotlin: Kotlin函数与Lambda表达式

最后更新:2026-08-26

函数是 Kotlin 的一等公民——高阶函数 + Lambda + 内联让 Charlie 用函数式风格构建 OrderProcessor 的数据处理流水线,代码量减少 60%。

1. 你将学到


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 调用链

100%
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 结果。

📖 小节


📝 作业

  1. 基础题(难度⭐):写一个 函数,用命名参数调用。提示:
  2. 进阶题(难度⭐⭐):用高阶函数实现 ,接收过滤条件和转换函数,返回结果列表。提示:<T>
  3. 挑战题(难度⭐⭐⭐):实现一个 <Any> 函数,从混合类型列表中过滤出指定类型的元素。提示:

← 上一课 | 下一课 →

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏