第 4 章 · 函数与 Lambda

5 个 demo 帮你理解默认参数、扩展函数、Lambda、闭包、inline 等核心特性。

🎛 默认参数 + 命名参数模拟器

勾选哪些参数你想"显式传递",看 Kotlin 怎么自动用默认值填补剩下的。

fun connect(
    host: String = "localhost",
    port: Int = 8080,
    timeout: Int = 5000,
    useTls: Boolean = false
)

📞 实际调用

🖨 执行结果

💡 对比 Java: Java 想实现"任意组合可选参数"需要写 2^4 = 16 个重载方法。 Kotlin 一行函数搞定。

🪄 扩展函数:让 String 多一个 toCamelCase 方法

☕ Java 写法

// 必须写工具类
public class StringUtils {
  public static String toCamelCase(String s) {
    String[] parts = s.split("_");
    StringBuilder sb = new StringBuilder();
    for (String p : parts) {
      sb.append(Character.toUpperCase(p.charAt(0)))
        .append(p.substring(1));
    }
    return sb.toString();
  }
}

// 调用 —— 别扭
String r = StringUtils
    .toCamelCase("hello_kotlin");

🟣 Kotlin 写法

// 直接给 String 类"扩展"方法
fun String.toCamelCase(): String =
    this.split("_").joinToString("") {
        it.replaceFirstChar { c -> c.uppercase() }
    }

// 调用 —— 跟内置方法一样
val r = "hello_kotlin".toCamelCase()
// → "HelloKotlin"

🧪 在线试试

.toCamelCase()

⚠️ 注意:扩展函数是静态分派

open class A
class B : A()

fun A.hello() = "I am A"
fun B.hello() = "I am B"

val a: A = B()
println(a.hello())   // 输出 "I am A"  ← 不是 "I am B"!
                     // 因为按编译期类型 A 决定调哪个

📈 Lambda 写法的"简化阶梯"

同一个需求:"把列表里每个元素 ×2"。看从最啰嗦的写法一步步演化到最简洁。

Step 0:Java 风格匿名内部类
list.map(new Function<Integer, Integer>() {
    @Override
    public Integer apply(Integer x) {
        return x * 2;
    }
});
Step 1:Kotlin Lambda 完整形式(带类型)
list.map({ x: Int -> x * 2 })
Step 2:去掉参数类型(编译器推断)
list.map({ x -> x * 2 })
Step 3:Lambda 是最后一个参数 → 拖出括号外
list.map() { x -> x * 2 }
Step 4:括号空了 → 直接省掉
list.map { x -> x * 2 }
Step 5:单参数 → 用 it 代替
list.map { it * 2 }

🔒 闭包:函数能"记住"它出生时的变量

fun makeCounter(): () -> Int {
    var count = 0
    return {
        count++
        count
    }
}

val c1 = makeCounter()
val c2 = makeCounter()  // 注意:每个 counter 有自己独立的 count

下面是两个独立的 counter,分别记着自己的 count,互不干扰。

Counter A

0

Counter B

0

每个 counter 都"捕获"了 makeCounter 函数里的 count 变量,但**两份拷贝独立**。 这就是闭包的本质:函数 + 它出生时所在环境的引用。

⚡ inline:让 Lambda 零运行时开销

普通高阶函数每次调用都要创建一个 Function 对象,频繁调用有 GC 压力。inline 让编译器把函数体和 lambda 直接嵌入调用点。

📝 你写的代码

inline fun <T> List<T>.fastForEach(
    action: (T) -> Unit
) {
    for (item in this) action(item)
}

// 调用
listOf(1, 2, 3).fastForEach {
    println(it)
}

🔬 编译后等价于

// fastForEach 调用消失了,直接展开:
val list = listOf(1, 2, 3)
for (item in list) {
    println(item)   // ← lambda 也内联了
}

// 没有 Function 对象创建
// 没有方法调用开销
📊 性能对比(百万次调用)
- 普通高阶函数:约 50ms(创建 1M 个 Function 对象 + GC)
- inline 高阶函数:约 5ms(无对象创建)
- 差距:~10x

📌 何时用 inline

  • ✅ 短小的高阶函数(< 30 行)
  • ✅ 频繁被调用(hot loop / 热路径)
  • ✅ 想支持 non-local return(lambda 里 return 跳出外层)
  • ❌ 函数体很大(编译产物会爆炸)
  • ❌ 没有 lambda 参数(inline 收益不大)
  • ❌ 递归调用(编译器拒绝)