第04篇-函数与Lambda表达式
【Kotlin + Spring Boot 4 从零到架构师】第 4 篇:函数与 Lambda 表达式
本系列定位:零基础入门,从 Kotlin 语法一路到 Spring Boot 4 高级架构(DDD + Modulith),适合 Java 开发者转型,也适合纯新手系统学习。
本篇你将学到
- 函数声明的完整语法:参数、返回值、默认参数、命名参数
- 单表达式函数的简洁写法
- Lambda 表达式的语法与高阶函数
it隐式参数的使用场景- Kotlin 集合操作函数:
map、filter、forEach、fold、reduce
学完本篇,你将能编写灵活的 Kotlin 函数,并运用 Lambda 表达式和集合操作函数写出远比 Java 简洁的数据处理代码。
一、函数声明
1.1 基本语法
Kotlin 用 fun 关键字声明函数:
// 完整语法:fun 函数名(参数名: 参数类型): 返回类型 { 函数体 }
fun add(a: Int, b: Int): Int {
return a + b
}
// 调用
val result = add(3, 5)
println(result) // 输出:8
对比 Java:
// Java
public static int add(int a, int b) {
return a + b;
}
Kotlin 函数不需要写在类里面,可以直接定义在文件顶层。这在后续 Spring Boot 开发中减少了大量样板代码。
1.2 无返回值的函数
使用 Unit 表示无返回值(类似 Java 的 void),Unit 可以省略:
// 显式声明 Unit
fun greet(name: String): Unit {
println("你好,$name!")
}
// 省略 Unit(效果相同,推荐)
fun greet(name: String) {
println("你好,$name!")
}
1.3 单表达式函数
当函数体只有一个表达式时,可以省略花括号和 return,用 = 直接赋值:
// 普通写法
fun double(x: Int): Int {
return x * 2
}
// 单表达式函数(推荐)
fun double(x: Int) = x * 2
// 返回类型也可以省略,编译器自动推断
fun square(x: Int) = x * x // 推断为 Int
fun isPositive(x: Int) = x > 0 // 推断为 Boolean
使用建议:简单的一行函数用
=写法更简洁;有复杂逻辑的函数仍用花括号。后续在 Spring Boot 的 Repository 层中,单表达式函数非常常见。
下面是函数声明的三种形式对比流程图:
二、高级函数特性
2.1 默认参数
Kotlin 允许为函数参数设置默认值,调用时可以省略这些参数——这替代了 Java 中需要写多个重载函数的场景:
fun createUser(
name: String,
age: Int = 0,
email: String = "未填写",
isActive: Boolean = true
) {
println("姓名:$name,年龄:$age,邮箱:$email,状态:${if (isActive) "激活" else "未激活"}")
}
// 调用——只传必须参数
createUser("张三")
// 输出:姓名:张三,年龄:0,邮箱:未填写,状态:激活
// 调用——传部分可选参数
createUser("李四", 25)
// 输出:姓名:李四,年龄:25,邮箱:未填写,状态:激活
// 调用——传所有参数
createUser("王五", 30, "wangwu@example.com", false)
// 输出:姓名:王五,年龄:30,邮箱:wangwu@example.com,状态:未激活
对比 Java——要实现同样的效果,Java 需要写 4 个重载方法:
// Java 需要大量重载
void createUser(String name) { createUser(name, 0, "未填写", true); }
void createUser(String name, int age) { createUser(name, age, "未填写", true); }
void createUser(String name, int age, String email) { createUser(name, age, email, true); }
void createUser(String name, int age, String email, boolean isActive) { /* ... */ }
2.2 命名参数
当函数有多个参数时,可以用 参数名 = 值 的方式传参,不必按顺序:
// 只修改 email,其他用默认值
createUser(name = "赵六", email = "zhaoliu@example.com")
// 输出:姓名:赵六,年龄:0,邮箱:zhaoliu@example.com,状态:激活
// 调整参数顺序
createUser(email = "test@test.com", name = "钱七", age = 28)
在 Spring Boot 中的应用:后续定义 DTO 和 Command 类时,大量使用命名参数 + 默认参数,代码极其简洁。
2.3 可变参数(vararg)
使用 vararg 修饰符,函数可以接受任意数量的参数:
fun sum(vararg numbers: Int): Int {
var total = 0
for (num in numbers) {
total += num
}
return total
}
// 传入任意数量的参数
println(sum(1, 2, 3)) // 6
println(sum(1, 2, 3, 4, 5)) // 15
println(sum()) // 0
// 如果已经有一个数组,用展开操作符 * 传入
val numbers = intArrayOf(10, 20, 30)
println(sum(*numbers)) // 60
vararg相当于 Java 的int...可变参数。在后续 Spring Boot 开发中,批量删除接口经常使用@PathVariable vararg ids: Long。
三、Lambda 表达式
Lambda 表达式是函数式编程的核心。简单来说,Lambda 就是一段可以作为参数传递的代码块,也可以被称为「匿名函数」。
3.1 Lambda 语法
完整的 Lambda 语法:
{ 参数列表 -> 函数体 }
// 完整写法:接受两个 Int 参数,返回它们的和
val sum: (Int, Int) -> Int = { a: Int, b: Int -> a + b }
// 调用
println(sum(3, 5)) // 输出:8
// 类型推断简化
val sum2 = { a: Int, b: Int -> a + b }
// 进一步简化(已知类型时)
val sum3: (Int, Int) -> Int = { a, b -> a + b }
3.2 无参数的 Lambda
val sayHello: () -> Unit = { println("Hello!") }
sayHello() // 输出:Hello!
3.3 单参数 Lambda 与 it 关键字
当 Lambda 只有一个参数时,可以省略参数声明,用 it 关键字代替:
val numbers = listOf(1, 2, 3, 4, 5)
// 完整写法
val doubled = numbers.map { n: Int -> n * 2 }
// 简化写法:用 it 代替唯一参数
val doubled2 = numbers.map { it * 2 }
println(doubled2) // 输出:[2, 4, 6, 8, 10]
it是 Kotlin 中一个非常重要的约定——当 Lambda 只有一个参数时,it自动指代该参数。这大大简化了集合操作代码。
3.4 Lambda 作为函数参数(高阶函数)
高阶函数就是接受 Lambda 作为参数或返回 Lambda 的函数:
// 定义高阶函数:接受一个 Lambda 作为操作
fun calculate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
return operation(a, b)
}
// 调用——传入不同的 Lambda
val result1 = calculate(10, 3) { x, y -> x + y } // 加法 → 13
val result2 = calculate(10, 3) { x, y -> x - y } // 减法 → 7
val result3 = calculate(10, 3) { x, y -> x * y } // 乘法 → 30
println("$result1, $result2, $result3") // 输出:13, 7, 30
注意语法细节:当函数的最后一个参数是 Lambda 时,Lambda 可以写在圆括号外面。如果 Lambda 是唯一的参数,圆括号也可以省略。这是 Kotlin 最优雅的语法糖之一。
3.5 从 Lambda 返回值
Lambda 的最后一行表达式就是返回值(和 if/when 表达式规则一致):
val process: (Int) -> String = { num ->
if (num > 0) {
"正数"
} else if (num < 0) {
"负数"
} else {
"零"
} // ← 最后一行就是返回值
}
println(process(42)) // 正数
println(process(-1)) // 负数
println(process(0)) // 零
下面是高阶函数调用过程的时序图:
四、集合操作函数
Lambda 表达式最常见的用途就是配合集合操作函数使用。这是 Kotlin 相对于 Java 最大的简洁性优势之一。
4.1 核心集合操作函数
以下操作都配合 Lambda 使用:
| 函数 | 作用 | Java 对应 |
|---|---|---|
map | 将每个元素转换为新元素 | stream().map() |
filter | 过滤符合条件的元素 | stream().filter() |
forEach | 遍历每个元素 | stream().forEach() |
find | 查找第一个匹配元素 | stream().findFirst() |
any | 是否有元素满足条件 | stream().anyMatch() |
all | 是否所有元素都满足条件 | stream().allMatch() |
none | 是否没有元素满足条件 | stream().noneMatch() |
count | 统计满足条件的元素数 | stream().filter().count() |
sortedBy | 按条件排序 | stream().sorted() |
groupBy | 按条件分组 | Collectors.groupingBy() |
fold | 从初始值开始累计 | stream().reduce() |
reduce | 累计所有元素 | stream().reduce() |
4.2 map — 转换元素
val numbers = listOf(1, 2, 3, 4, 5)
// 每个元素乘以 2
val doubled = numbers.map { it * 2 }
println(doubled) // [2, 4, 6, 8, 10]
// 转换类型:Int → String
val strings = numbers.map { "数字:$it" }
println(strings) // [数字:1, 数字:2, 数字:3, 数字:4, 数字:5]
// 提取对象属性
data class Product(val name: String, val price: Double)
val products = listOf(
Product("键盘", 199.0),
Product("鼠标", 89.0),
Product("显示器", 1299.0)
)
val names = products.map { it.name }
println(names) // [键盘, 鼠标, 显示器]
val prices = products.map { it.price }
println(prices) // [199.0, 89.0, 1299.0]
4.3 filter — 过滤元素
val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
// 过滤偶数
val evens = numbers.filter { it % 2 == 0 }
println(evens) // [2, 4, 6, 8, 10]
// 过滤大于 5 的数
val big = numbers.filter { it > 5 }
println(big) // [6, 7, 8, 9, 10]
// 过滤商品中价格 > 100 的
val products = listOf(
Product("键盘", 199.0),
Product("鼠标", 89.0),
Product("显示器", 1299.0)
)
val expensive = products.filter { it.price > 100.0 }
println(expensive.map { it.name }) // [键盘, 显示器]
4.4 链式调用
集合操作函数可以链式调用,像流水线一样处理数据:
val products = listOf(
Product("键盘", 199.0),
Product("鼠标", 89.0),
Product("显示器", 1299.0),
Product("耳机", 299.0),
Product("鼠标垫", 19.0)
)
// 链式操作:筛选 → 转换 → 排序
val result = products
.filter { it.price > 50.0 } // 过滤价格 > 50
.map { it.name to it.price } // 转换为 Pair(名称, 价格)
.sortedByDescending { it.second } // 按价格降序排列
result.forEach { (name, price) ->
println("$name:¥$price")
}
// 输出:
// 显示器:¥1299.0
// 耳机:¥299.0
// 键盘:¥199.0
对比 Java 等价代码:
// Java 等价实现——明显更繁琐
List<Map.Entry<String, Double>> result = products.stream()
.filter(p -> p.getPrice() > 50.0)
.map(p -> Map.entry(p.getName(), p.getPrice()))
.sorted((a, b) -> Double.compare(b.getValue(), a.getValue()))
.collect(Collectors.toList());
4.5 groupBy — 分组
data class Student(val name: String, val grade: String, val score: Int)
val students = listOf(
Student("张三", "一班", 90),
Student("李四", "二班", 85),
Student("王五", "一班", 78),
Student("赵六", "二班", 92),
Student("钱七", "一班", 65)
)
// 按班级分组
val byGrade = students.groupBy { it.grade }
byGrade.forEach { (grade, studentList) ->
println("$grade:${studentList.map { it.name }}")
}
// 输出:
// 一班:[张三, 王五, 钱七]
// 二班:[李四, 赵六]
// 按是否及格分组
val byPass = students.groupBy { it.score >= 60 }
println("及格:${byPass[true]?.map { it.name }}") // [张三, 李四, 王五, 赵六, 钱七]
4.6 fold 与 reduce — 聚合
val numbers = listOf(1, 2, 3, 4, 5)
// reduce:无初始值,从第一个元素开始累计
val sum = numbers.reduce { acc, num -> acc + num }
println(sum) // 15
// fold:有初始值,更灵活
val sumFrom10 = numbers.fold(10) { acc, num -> acc + num }
println(sumFrom10) // 25(10 + 1 + 2 + 3 + 4 + 5)
// fold 可以累计为不同类型
val concatenated = numbers.fold("数字:") { acc, num -> "$acc$num " }
println(concatenated) // 数字:1 2 3 4 5
4.7 常用判断函数
val numbers = listOf(1, 2, 3, 4, 5)
// any:是否有满足条件的元素
println(numbers.any { it > 3 }) // true
println(numbers.any { it > 10 }) // false
// all:是否所有元素都满足条件
println(numbers.all { it > 0 }) // true
println(numbers.all { it > 3 }) // false
// none:是否没有元素满足条件
println(numbers.none { it < 0 }) // true
// count:统计满足条件的元素数
println(numbers.count { it % 2 == 0 }) // 2
// find:查找第一个匹配元素(返回可空类型)
val firstEven = numbers.find { it % 2 == 0 }
println(firstEven) // 2
下面是集合操作链式调用的数据流图:
五、实战练习
练习一:商品数据处理
data class Product(val name: String, val category: String, val price: Double, val stock: Int)
fun main() {
val products = listOf(
Product("机械键盘", "外设", 299.0, 50),
Product("蓝牙鼠标", "外设", 89.0, 0),
Product("27寸显示器", "显示", 1599.0, 15),
Product("USB-C 扩展坞", "配件", 199.0, 100),
Product("无线耳机", "音频", 599.0, 30),
Product("有线耳机", "音频", 49.0, 0)
)
// 1. 找出所有有库存的商品名称
val inStock = products
.filter { it.stock > 0 }
.map { it.name }
println("有库存:$inStock")
// 输出:有库存:[机械键盘, 27寸显示器, USB-C 扩展坞, 无线耳机]
// 2. 按类别分组,计算每类商品的平均价格
val avgPriceByCategory = products
.groupBy { it.category }
.mapValues { (_, list) ->
list.map { it.price }.average()
}
avgPriceByCategory.forEach { (category, avg) ->
println("$category 平均价格:¥${"%.2f".format(avg)}")
}
// 输出:
// 外设 平均价格:¥194.00
// 显示 平均价格:¥1599.00
// 配件 平均价格:¥199.00
// 音频 平均价格:¥324.00
// 3. 找到价格最高的商品
val mostExpensive = products.maxByOrNull { it.price }
println("最贵商品:${mostExpensive?.name}") // 27寸显示器
// 4. 计算库存总值
val totalValue = products.fold(0.0) { acc, p ->
acc + p.price * p.stock
}
println("库存总值:¥$totalValue")
}
练习二:单词统计器
fun main() {
val sentence = "kotlin is great and kotlin is powerful and kotlin is easy"
// 统计每个单词出现的次数
val wordCount = sentence
.split(" ")
.groupBy { it }
.mapValues { it.value.size }
.toList()
.sortedByDescending { it.second }
wordCount.forEach { (word, count) ->
println("$word: $count 次")
}
// 输出:
// kotlin: 3 次
// is: 3 次
// and: 2 次
// great: 1 次
// powerful: 1 次
// easy: 1 次
}
阅读建议:这张图总结了全篇所有知识点及其层级关系。你可以对照此图逐节学习,也可以在学习完某节后回顾图中对应分支,检查自己是否掌握了所有子知识点。
本篇小结
| 知识点 | 核心内容 |
|---|---|
| 函数声明 | fun name(params): ReturnType { body } |
| 单表达式函数 | fun double(x: Int) = x * 2 |
| 默认参数 | fun f(name: String, age: Int = 0) |
| 命名参数 | f(name = "Tom", age = 20) |
| 可变参数 | fun sum(vararg nums: Int) |
| Lambda 语法 | { a, b -> a + b } |
it 关键字 | 单参数 Lambda 的隐式参数名 |
| 高阶函数 | 接受 Lambda 作为参数的函数 |
| 尾随 Lambda | 最后一个参数是 Lambda 时可写在括号外 |
map | 转换每个元素 |
filter | 过滤元素 |
groupBy | 按条件分组 |
fold / reduce | 聚合累计 |
下篇预告
Kotlin 的
data class用一行代码就能替代 Java 几十行的 POJO。下一篇正式进入面向对象编程,学习类的声明、属性、构造函数、可见性修饰符等核心概念。
如果本篇内容对你有帮助,欢迎点赞收藏!有任何疑问,欢迎在评论区交流。
更多推荐
所有评论(0)