前置知识: Java

Kotlin 函数与 Lambda

4 min中级

函数定义、扩展函数、Lambda 表达式、高阶函数、内联函数与 SAM 转换。

前置知识

学习目标

  • 掌握「1. 函数定义」的核心机制、典型用法与常见陷阱
  • 掌握「2. 扩展函数」的核心机制、典型用法与常见陷阱
  • 掌握「3. Lambda 表达式」的核心机制、典型用法与常见陷阱
  • 掌握「4. 高阶函数」的核心机制、典型用法与常见陷阱
  • 掌握「5. 内联函数」的核心机制、典型用法与常见陷阱

1. 函数定义

1.1 基本函数

// 标准函数
fun add(a: Int, b: Int): Int {
    return a + b
}

// 表达式函数体
fun add(a: Int, b: Int): Int = a + b

// 返回类型推断
fun add(a: Int, b: Int) = a + b  // 推断为 Int

// 无返回值(Unit)
fun greet(name: String): Unit {
    println("Hello, $name!")
}
// Unit 可省略
fun greet(name: String) {
    println("Hello, $name!")
}

1.2 默认参数

fun connect(host: String, port: Int = 8080, timeout: Int = 5000): String {
    return "$host:$port (timeout: ${timeout}ms)"
}

connect("localhost")                    // localhost:8080 (timeout: 5000ms)
connect("localhost", 3306)              // localhost:3306 (timeout: 5000ms)
connect("localhost", 3306, 10000)       // localhost:3306 (timeout: 10000ms)

1.3 命名参数

fun createUser(name: String, age: Int = 0, email: String = "", active: Boolean = true) {
    // ...
}

// 使用命名参数跳过中间参数
createUser("Alice", email = "alice@example.com")
createUser(name = "Bob", active = false)

最佳实践:当函数有多个参数时,尤其是布尔类型参数,使用命名参数可大幅提升代码可读性。

1.4 可变参数

fun sum(vararg numbers: Int): Int {
    return numbers.sum()
}

sum(1, 2, 3)          // 6
sum(1, 2, 3, 4, 5)    // 15

// 展开数组
val array = intArrayOf(1, 2, 3)
sum(*array)            // 使用 * 展开运算符

1.5 尾递归函数

tailrec fun factorial(n: Long, acc: Long = 1): Long {
    return if (n <= 1) acc else factorial(n - 1, acc * n)
}

factorial(20)  // 2432902008176640000

2. 扩展函数

扩展函数允许为已有类添加新方法,无需继承或修改源码:

// 为 String 添加扩展函数
fun String.addExclamation(): String = this + "!"

println("Hello".addExclamation())  // Hello!

// 为 Int 添加扩展
fun Int.isEven(): Boolean = this % 2 == 0
println(4.isEven())  // true
println(3.isEven())  // false

// 为 List 添加扩展
fun <T> List<T>.second(): T = this[1]
val list = listOf("a", "b", "c")
println(list.second())  // b

2.1 可空接收者扩展

// 可空接收者 — 在函数内部处理 null
fun String?.isNullOrEmpty(): Boolean = this == null || this.isEmpty()

val s: String? = null
s.isNullOrEmpty()  // true

2.2 扩展属性

val String.halfLength: Int
    get() = this.length / 2

println("Kotlin".halfLength)  // 3

var StringBuilder.lastChar: Char
    get() = this[this.length - 1]
    set(value) { this.append(value) }

2.3 扩展函数的解析

扩展函数是静态解析的,不是虚函数:

open class Animal
class Dog : Animal()

fun Animal.sound() = "Generic sound"
fun Dog.sound() = "Woof"

fun makeSound(animal: Animal) {
    println(animal.sound())  // 静态解析,调用 Animal.sound()
}

val dog: Dog = Dog()
val animal: Animal = dog

makeSound(dog)     // "Generic sound"(不是 "Woof"!)
dog.sound()        // "Woof"
animal.sound()     // "Generic sound"

3. Lambda 表达式

3.1 基本语法

// 完整语法
val sum: (Int, Int) -> Int = { a: Int, b: Int -> a + b }

// 类型推断
val sum = { a: Int, b: Int -> a + b }

// 指定类型省略参数类型
val sum: (Int, Int) -> Int = { a, b -> a + b }

3.2 it 隐式参数

当 Lambda 只有一个参数时,可用 it 代替:

val square: (Int) -> Int = { it * it }
val isEven: (Int) -> Boolean = { it % 2 == 0 }

val numbers = listOf(1, 2, 3, 4, 5)
numbers.filter { it > 3 }      // [4, 5]
numbers.map { it * 2 }          // [2, 4, 6, 8, 10]

3.3 Lambda 与集合操作

val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

// filter — 过滤
val evens = numbers.filter { it % 2 == 0 }

// map — 映射
val squares = numbers.map { it * it }

// forEach — 遍历
numbers.forEach { println(it) }

// fold — 累积
val sum = numbers.fold(0) { acc, num -> acc + num }

// groupBy — 分组
val grouped = numbers.groupBy { if (it % 2 == 0) "even" else "odd" }

// sortedBy — 排序
val sorted = numbers.sortedByDescending { it }

4. 高阶函数

高阶函数是以函数作为参数或返回值的函数:

// 函数作为参数
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
}

numbers.customFilter { it > 5 }

// 函数作为返回值
fun multiplier(factor: Int): (Int) -> Int = { number -> number * factor }

val double = multiplier(2)
val triple = multiplier(3)
double(5)   // 10
triple(5)   // 15

4.1 函数类型

// 函数类型语法
val f1: () -> Unit = { println("No params") }
val f2: (Int) -> String = { "Number: $it" }
val f3: (Int, String) -> Boolean = { num, str -> num == str.length }

// 带接收者的函数类型
val f4: String.(Int) -> String = { this.repeat(it) }
"Ha".f4(3)        // "HaHaHa"
"Ha".let { f4(it, 3) }  // 等价写法

// 函数类型实例化
val f5 = fun(a: Int, b: Int): Int = a + b  // 匿名函数

4.2 常见高阶函数模式

// also — 执行附加操作,返回原对象
val user = User("Alice", 25).also {
    println("Created: $it")
}

// apply — 配置对象,返回原对象
val builder = StringBuilder().apply {
    append("Hello")
    append(", ")
    append("Kotlin")
}

// let — 转换对象,返回 Lambda 结果
val length = "Kotlin".let {
    println("Processing: $it")
    it.length
}

// run — 执行代码块,返回结果
val result = "Kotlin".run {
    println("Processing: $this")
    length
}

// with — 非扩展版本的 run
val greeting = with(StringBuilder()) {
    append("Hello")
    append(", Kotlin")
    toString()
}

5. 内联函数

5.1 inline 关键字

高阶函数会为 Lambda 创建匿名内部类对象,产生运行时开销。inline 将函数体直接内联到调用处:

inline fun <T> measureTime(block: () -> T): T {
    val start = System.currentTimeMillis()
    val result = block()
    val end = System.currentTimeMillis()
    println("Execution time: ${end - start}ms")
    return result
}

// 调用处展开后等价于:
// val start = System.currentTimeMillis()
// val result = /* block 内容 */
// val end = System.currentTimeMillis()

5.2 noinline 与 crossinline

// noinline — 禁止内联特定参数
inline fun process(
    inlineBlock: () -> Unit,      // 被内联
    noinline notInlined: () -> Unit  // 不被内联
) {
    inlineBlock()
    notInlined()
}

// crossinline — 允许内联但禁止非局部返回
inline fun runInThread(crossinline action: () -> Unit) {
    Thread { action() }.start()
    // action 中不能使用 return 退出外层函数
}

5.3 非局部返回

fun processElements(elements: List<Int>) {
    elements.forEach {
        if (it == 0) return  // 非局部返回,退出 processElements
        println(it)
    }
    println("Done")  // 如果遇到 0,这行不会执行
}

6. SAM 转换

Kotlin 支持对 Java 单抽象方法(SAM)接口的自动转换:

// Java 接口
// public interface OnClickListener {
//     void onClick(View v);
// }

// Kotlin 中使用 SAM 转换
button.setOnClickListener { view ->
    println("Clicked: $view")
}

// 显式 SAM 转换(Kotlin 1.4+ 也支持 Kotlin 函数式接口)
fun interface Producer<T> {
    fun produce(): T
}

val producer = Producer { "Hello" }
producer.produce()  // "Hello"

6.1 Kotlin 函数式接口

// fun interface — Kotlin 1.4+ 支持
fun interface Transformer<T, R> {
    fun transform(input: T): R
}

// SAM 转换
val toLength: Transformer<String, Int> = Transformer { it.length }
toLength.transform("Kotlin")  // 6

// 多个 Lambda 参数时指定哪个进行 SAM 转换
fun interface ClickHandler {
    fun onClick()
}

fun setup(handler: ClickHandler, delay: Long = 0) {
    // ...
}

setup(ClickHandler { println("Clicked!") })

7. 作用域函数对比

函数对象引用返回值是否扩展函数典型场景
letitLambda 结果是空安全操作、链式转换
runthisLambda 结果是对象初始化并计算结果
withthisLambda 结果否(参数)对已存在对象进行操作
applythis对象本身是对象初始化/配置
alsoit对象本身是附加操作、日志、验证
// 典型使用场景
val person = Person().apply {
    name = "Alice"
    age = 25
}.also {
    println("Created person: $it")
}

val nameLength = person.let {
    it.name.length
}

选择建议:需要返回对象本身用 apply/also,需要返回计算结果用 let/run/with;引用用 this 还是 it 取决于是否需要频繁访问对象成员。

函数定义

基本写法:标准函数 fun <name>(<params>): <ReturnType> { <body> }

// 标准函数定义
fun add(a: Int, b: Int): Int {
    return a + b;
}

基本写法:表达式函数体 fun <name>(<params>): <ReturnType> = <expr>

// 表达式函数体
fun add(a: Int, b: Int): Int = a + b;

基本写法:表达式函数体类型推断 fun <name>(<params>) = <expr>

// 返回类型推断为 Int
fun add(a: Int, b: Int) = a + b;

基本写法:Unit 无返回值函数 fun <name>(<params>): Unit { <body> }

// 无返回值(Unit)
fun greet(name: String): Unit {
    println("Hello, $name!");
}

基本写法:省略 Unit 的无返回值函数 fun <name>(<params>) { <body> }

// Unit 可省略
fun greet(name: String) {
    println("Hello, $name!");
}

单行写法:默认参数函数 fun <name>(<param1>: <Type>, <param2>: <Type> = <default>): <ReturnType>

// 单行声明带默认参数的函数
fun connect(host: String, port: Int = 8080): String = "$host:$port";

换行写法:多默认参数函数 fun <name>(<param1>: <Type>, <param2>: <Type> = <default>, <param3>: <Type> = <default>): <ReturnType>

// 换行声明多个默认参数
fun connect(
    host: String,
    port: Int = 8080,
    timeout: Int = 5000
): String {
    return "$host:$port (timeout: ${timeout}ms)";
}

基本写法:命名参数调用 <name>(<arg> = <value>)

// 使用命名参数跳过中间参数
createUser("Alice", email = "alice@example.com");

基本写法:可变参数函数 fun <name>(vararg <params>: <Type>): <ReturnType>

// 可变参数函数
fun sum(vararg numbers: Int): Int {
    return numbers.sum();
}

基本写法:展开数组调用可变参数 <name>(*<array>)

// 使用 * 展开运算符
val array = intArrayOf(1, 2, 3);
sum(*array);

基本写法:尾递归函数 tailrec fun <name>(<params>): <ReturnType>

// 尾递归函数,避免栈溢出
tailrec fun factorial(n: Long, acc: Long = 1): Long {
    return if (n <= 1) acc else factorial(n - 1, acc * n);
}

扩展函数

基本写法:为类添加扩展函数 fun <ReceiverType>.<name>(<params>): <ReturnType>

// 为 String 添加扩展函数
fun String.addExclamation(): String = this + "!";

基本写法:为 Int 添加扩展函数 fun Int.<name>(): <ReturnType>

// 为 Int 添加扩展
fun Int.isEven(): Boolean = this % 2 == 0;

基本写法:可空接收者扩展 fun <ReceiverType>?.<name>(<params>): <ReturnType>

// 可空接收者,在函数内部处理 null
fun String?.isNullOrEmpty(): Boolean = this == null || this.isEmpty();

基本写法:只读扩展属性 val <ReceiverType>.<name>: <Type> get() = <expr>

// 为 String 添加只读扩展属性
val String.halfLength: Int
    get() = this.length / 2;

换行写法:可变扩展属性 var <ReceiverType>.<name>: <Type> [get() = <expr>] [set(value) { <body> }]

// 为 StringBuilder 添加可变扩展属性
var StringBuilder.lastChar: Char
    get() = this[this.length - 1]
    set(value) { this.append(value); }

基本写法:扩展函数静态解析 fun <BaseType>.<name>() = <expr>

// 扩展函数静态解析,由声明类型决定
open class Animal;
class Dog : Animal();
fun Animal.sound() = "Generic sound";
fun Dog.sound() = "Woof";
val animal: Animal = Dog();
animal.sound();  // "Generic sound"

Lambda 表达式

基本写法:完整语法 Lambda val <name>: (<ParamTypes>) -> <ReturnType> = { <params> -> <body> }

// 完整语法
val sum: (Int, Int) -> Int = { a: Int, b: Int -> a + b };

基本写法:类型推断 Lambda val <name> = { <params> -> <body> }

// 类型推断
val sum = { a: Int, b: Int -> a + b };

基本写法:指定类型省略参数类型 val <name>: (<ParamTypes>) -> <ReturnType> = { <params> -> <body> }

// 指定函数类型,省略参数类型
val sum: (Int, Int) -> Int = { a, b -> a + b };

基本写法:it 隐式参数 { <body using it> }

// 单参数 Lambda 用 it 代替
val square: (Int) -> Int = { it * it };

基本写法:filter 过滤 <collection>.filter { <predicate> }

// filter 过滤集合
val numbers = listOf(1, 2, 3, 4, 5);
numbers.filter { it > 3 };

基本写法:map 映射 <collection>.map { <transform> }

// map 映射集合元素
numbers.map { it * 2 };

基本写法:forEach 遍历 <collection>.forEach { <body> }

// forEach 遍历集合
numbers.forEach { println(it); }

基本写法:fold 累积 <collection>.fold(<init>) { <acc>, <item> -> <body> }

// fold 带初始值累积
val sum = numbers.fold(0) { acc, num -> acc + num };

基本写法:groupBy 分组 <collection>.groupBy { <keySelector> }

// groupBy 按条件分组
val grouped = numbers.groupBy { if (it % 2 == 0) "even" else "odd" };

高阶函数

基本写法:函数作为参数 fun <name>(<param>: (<Type>) -> <ReturnType>): <ReturnType>

// 接受函数作为参数
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 <name>(<params>): (<Type>) -> <ReturnType>

// 返回函数
fun multiplier(factor: Int): (Int) -> Int = { number -> number * factor };

基本写法:无参函数类型 val <name>: () -> <ReturnType> = { <body> }

// 无参函数类型
val f1: () -> Unit = { println("No params"); };

基本写法:带参函数类型 val <name>: (<Type>) -> <ReturnType> = { <body> }

// 带参函数类型
val f2: (Int) -> String = { "Number: $it" };

基本写法:带接收者的函数类型 val <name>: <ReceiverType>.(<Type>) -> <ReturnType> = { <body> }

// 带接收者的函数类型
val f4: String.(Int) -> String = { this.repeat(it) };

基本写法:匿名函数 val <name> = fun(<params>): <ReturnType> = <expr>

// 匿名函数
val f5 = fun(a: Int, b: Int): Int = a + b;

基本写法:also 执行附加操作 <obj>.also { <body with it> }

// also 返回原对象
val user = User("Alice", 25).also {
    println("Created: $it");
};

基本写法:apply 配置对象 <obj>.apply { <body with this> }

// apply 返回原对象
val builder = StringBuilder().apply {
    append("Hello");
    append(", Kotlin");
};

基本写法:let 转换对象 <obj>.let { <body with it> }

// let 返回 Lambda 结果
val length = "Kotlin".let { it.length };

基本写法:run 执行代码块 <obj>.run { <body with this> }

// run 返回 Lambda 结果
val result = "Kotlin".run { length };

基本写法:with 执行多个操作 with(<obj>) { <body with this> }

// with 非扩展版本
val greeting = with(StringBuilder()) {
    append("Hello");
    append(", Kotlin");
    toString();
};

内联函数

基本写法:inline 内联函数 inline fun <name>(<params>): <ReturnType>

// inline 关键字内联函数
inline fun <T> measureTime(block: () -> T): T {
    val start = System.currentTimeMillis();
    val result = block();
    val end = System.currentTimeMillis();
    println("Execution time: ${end - start}ms");
    return result;
}

换行写法:noinline 与 crossinline inline fun <name>(<param1>: () -> Unit, noinline <param2>: () -> Unit)

// noinline 禁止内联,crossinline 禁止非局部返回
inline fun process(
    inlineBlock: () -> Unit,
    noinline notInlined: () -> Unit
) {
    inlineBlock();
    notInlined();
}

基本写法:crossinline 禁止非局部返回 inline fun <name>(crossinline <param>: () -> Unit)

// crossinline 允许内联但禁止非局部返回
inline fun runInThread(crossinline action: () -> Unit) {
    Thread { action(); }.start();
}

基本写法:非局部返回 <collection>.forEach { if (<cond>) return; <body> }

// 非局部返回,退出外层函数
fun processElements(elements: List<Int>) {
    elements.forEach {
        if (it == 0) return;
        println(it);
    }
}

SAM 转换

基本写法:Java SAM 接口转换 <obj>.setListener { <param> -> <body> }

// SAM 转换简化 Java 接口实现
button.setOnClickListener { view ->
    println("Clicked: $view");
}

基本写法:Kotlin 函数式接口 fun interface <Name> { fun <method>(<params>): <ReturnType> }

// fun interface 声明
fun interface Producer<T> {
    fun produce(): T;
}

基本写法:函数式接口实例化 val <name> = <Interface> { <body> }

// 函数式接口实例化
val producer = Producer { "Hello" };

单行写法:多类型参数函数式接口 fun interface <Name><<T1>, <T2>> { fun <method>(<param>: <T1>): <T2> }

// 多类型参数函数式接口
fun interface Transformer<T, R> {
    fun transform(input: T): R;
}

作用域函数对比

基本写法:apply 配置并返回对象 <obj>.apply { <body with this> }

// apply 典型场景:配置对象
val person = Person().apply {
    name = "Alice";
    age = 25;
};

基本写法:also 附加操作并返回对象 <obj>.also { <body with it> }

// also 典型场景:日志记录
val logged = person.also {
    println("Created person: $it");
};

基本写法:let 转换并返回结果 <obj>.let { <body with it> }

// let 典型场景:转换
val nameLength = person.let { it.name.length };