函数是 Kotlin 代码的核心组成单元,它封装了可复用的逻辑,让代码更具可读性和可维护性。本节课将从函数的基础定义出发,逐步深入到 Lambda 表达式与高阶函数的应用,帮你全面掌握 Kotlin 函数的核心用法。
一、函数的基础定义:5 个核心组成部分
Kotlin 中函数的定义通过 fun
关键字开启,完整结构包含 fun 关键字、函数名、参数列表、返回值类型、函数体 五部分,语法如下
Kotlin
fun 函数名(参数名1: 类型1, 参数名2: 类型2, ...): 返回值类型 {
// 函数体(代码块)
return 结果 // 若返回值类型非Unit,需return
}
1. 各组成部分详解
- fun 关键字 :固定开头,标记这是一个函数(类似 Java 的
public static
,但更简洁)。 - 函数名 :遵循 "首字母小写 + 驼峰式" 规范(如
calculateSum
,而非CalculateSum
),见名知意。 - 参数列表 :格式为 "
参数名: 类型
",多个参数用逗号分隔(注意:Kotlin 是 "参数名在前,类型在后",和 Java 相反)。 - 返回值类型 :用
:
跟在参数列表后;若函数无返回值,可写Unit
(类似 Java 的void
)或直接省略。 - 函数体 :分两种形式 ------代码块函数 (用
{}
包裹多行代码)和 单表达式函数 (用=
连接单个表达式,简化代码)。
2. 实战示例:不同类型的函数
(1)有返回值的代码块函数
计算两个整数的和,返回结果:
Kotlin
// 函数名:calculateSum,参数:a(Int)、b(Int),返回值类型:Int
fun calculateSum(a: Int, b: Int): Int {
val result = a + b
return result // 显式return返回结果
}
(2)无返回值的函数(省略 Unit)
打印用户信息,无返回值:
Kotlin
// 省略返回值类型(等价于 ": Unit"),无需return
fun printUserInfo(name: String, age: Int) {
println("姓名:$name,年龄:$age")
}
(3)单表达式函数(简化代码)
若函数体只有一个表达式,可用 =
代替 {}
,甚至省略返回值类型(Kotlin 会自动推断):
Kotlin
// 简化1:用=代替{},保留返回值类型
fun multiply(a: Int, b: Int): Int = a * b
// 简化2:省略返回值类型(Kotlin 推断出返回Int)
fun subtract(a: Int, b: Int) = a - b
二、函数的调用与重载:灵活复用函数
定义函数后,需要通过 "调用" 执行其逻辑;而 "重载""默认参数""命名参数" 则能让函数调用更灵活。
1. 函数调用:简单直接
语法:函数名(参数值1, 参数值2, ...)
,按参数列表顺序传值即可:
Kotlin
// 调用calculateSum,接收返回值
val sum = calculateSum(3, 5)
println(sum) // 输出:8
// 调用printUserInfo,无返回值
printUserInfo("小明", 20) // 输出:姓名:小明,年龄:20
2. 函数重载:同名不同参
重载(Overload) 指:多个函数同名,但参数列表(参数个数、参数类型)不同。Kotlin 会根据调用时的参数自动匹配对应函数。
示例:定义 3 个同名的 add
函数,处理不同参数场景:
Kotlin
// 1. 两个Int相加
fun add(a: Int, b: Int) = a + b
// 2. 三个Int相加(参数个数不同)
fun add(a: Int, b: Int, c: Int) = a + b + c
// 3. 两个Double相加(参数类型不同)
fun add(a: Double, b: Double) = a + b
// 调用时自动匹配
println(add(1, 2)) // 匹配1:输出3
println(add(1, 2, 3)) // 匹配2:输出6
println(add(1.5, 2.5)) // 匹配3:输出4.0
⚠️ 注意:返回值类型不同不能作为重载的条件 (如 fun add(a: Int, b: Int): Int
和 fun add(a: Int, b: Int): Double
会报错)。
3. 默认参数:减少重载次数
给参数设置默认值,调用时可省略该参数,避免定义多个重载函数。
示例:定义 printOrder
函数,给 "数量" 和 "地址" 设默认值:
Kotlin
// 数量默认1,地址默认"未知地址"
fun printOrder(goods: String, count: Int = 1, address: String = "未知地址") {
println("商品:$goods,数量:$count,地址:$address")
}
// 调用方式1:传所有参数
printOrder("手机", 2, "北京市") // 输出:商品:手机,数量:2,地址:北京市
// 调用方式2:省略默认参数(数量用默认1)
printOrder("耳机", address = "上海市") // 输出:商品:耳机,数量:1,地址:上海市
// 调用方式3:省略所有默认参数
printOrder("充电器") // 输出:商品:充电器,数量:1,地址:未知地址
4. 命名参数:提高代码可读性
当函数参数较多时,调用时可显式指定 "参数名 = 值",无需按顺序传参,避免传错位置。
示例:调用一个多参数函数,用命名参数明确含义:
Kotlin
// 定义一个计算商品总价的函数
fun calculateTotal(goodsPrice: Double, discount: Double = 0.0, tax: Double = 0.1) =
(goodsPrice - discount) * (1 + tax)
// 问题:直接传值,分不清0.5和0.08分别对应哪个参数
val total1 = calculateTotal(100.0, 0.5, 0.08)
// 优化:用命名参数,可读性大幅提升
val total2 = calculateTotal(
goodsPrice = 100.0,
tax = 0.08, // 打乱顺序也没问题
discount = 0.5
)
println(total2) // 输出:(100-0.5)*(1+0.08) = 107.46
三、特殊函数:满足多样化场景
除了普通函数,Kotlin 还提供了几种特殊函数,用于简化特定场景的代码。
1. 单表达式函数(再深化)
前面已提过,这里补充两个细节:
- 若表达式是
if-else
,也可作为单表达式(无需{}
):
Kotlin
// 判断是否为成年人,单表达式+if-else
fun isAdult(age: Int) = if (age >= 18) "成年人" else "未成年人"
println(isAdult(20)) // 输出:成年人
- 若返回值是泛型或复杂类型,建议显式写返回值类型(避免推断错误):
Kotlin
// 显式指定返回值类型为 List<String>
fun getFruits(): List<String> = listOf("苹果", "香蕉", "橙子")
2. 可变参数函数:接收任意个数的参数
用 vararg
关键字标记参数,可接收 "0 个或多个同类型参数",类似 Java 的 ...
。
示例:计算任意个数整数的和:
Kotlin
// vararg 标记参数,接收多个Int
fun sumVararg(vararg numbers: Int): Int {
var total = 0
// 可变参数可直接遍历(内部会转为数组)
for (num in numbers) {
total += num
}
return total
}
// 调用方式1:传多个独立值
println(sumVararg(1, 2, 3)) // 输出:6
// 调用方式2:传数组(需用 * 展开数组,否则会把数组当作一个参数)
val arr = intArrayOf(4, 5, 6)
println(sumVararg(*arr)) // 输出:15(* 是展开运算符)
3. 局部函数:函数内部定义函数
在一个函数内部定义另一个函数,称为 "局部函数"。局部函数只能在外部函数内部调用,用于封装内部逻辑,避免外部访问。
示例:计算订单总价,内部用局部函数处理折扣:
Kotlin
fun calculateOrderTotal(price: Double, quantity: Int, memberLevel: String): Double {
// 局部函数:仅在 calculateOrderTotal 内部可用
fun getDiscount(): Double {
return when (memberLevel) {
"VIP" -> 0.2 // VIP折扣20%
"普通会员" -> 0.1 // 普通会员折扣10%
else -> 0.0 // 非会员无折扣
}
}
val discount = getDiscount() // 调用局部函数
return price * quantity * (1 - discount)
}
// 调用外部函数
val total = calculateOrderTotal(100.0, 3, "VIP")
println(total) // 输出:100*3*(1-0.2) = 240.0
4. 顶层函数:无需类包裹的函数
Kotlin 允许在 "文件顶层" 定义函数(不嵌套在类或对象中),直接通过函数名调用,无需像 Java 那样依赖类名(如 Util.formatTime()
)。
示例:在 Util.kt
文件中定义顶层函数:
Kotlin
// Util.kt 文件(无类包裹)
fun formatTime(seconds: Int): String {
val minute = seconds / 60
val sec = seconds % 60
return String.format("%02d:%02d", minute, sec)
}
在其他文件中直接调用:
Kotlin
// 无需导入类,直接调用顶层函数
val time = formatTime(125)
println(time) // 输出:02:05(125秒=2分5秒)
⚠️ 注意:顶层函数会被编译成静态方法(对应 Java 的 static
),但调用时更简洁。
四、Lambda 表达式:简化函数参数
Lambda 是 "匿名函数" 的简写形式,核心作用是用简洁的语法传递代码块,常用于作为函数参数(为高阶函数铺路)。
1. Lambda 语法结构
基本语法:{ 参数列表 -> 代码块 }
,各部分含义:
{}
:包裹 Lambda 整体;参数列表
:格式为 "参数名: 类型
"(若类型可推断,可省略类型);->
:分隔参数列表和代码块;代码块
:Lambda 的逻辑,最后一行表达式的结果即为 Lambda 的返回值 (无需return
)。
示例:定义一个 "计算两数乘积" 的 Lambda:
Kotlin
// 完整语法:(Int, Int) -> Int 表示 Lambda 的类型(接收两个Int,返回Int)
val multiply: (Int, Int) -> Int = { a: Int, b: Int -> a * b }
// 简化语法:省略参数类型(Kotlin 推断 a、b 为Int)
val multiplySimple: (Int, Int) -> Int = { a, b -> a * b }
// 调用 Lambda(类似函数调用)
println(multiply(3, 5)) // 输出:15
println(multiplySimple(4, 6)) // 输出:24
2. Lambda 的简化规则
Kotlin 对 Lambda 有多重简化规则,让代码更短:
规则 1:参数类型可推断时,省略类型
如上面的 multiplySimple
,因已指定 Lambda 类型为 (Int, Int) -> Int
,所以 a
和 b
的类型可省略。
规则 2:只有一个参数时,可省略参数列表,用 it
代替
若 Lambda 只有一个参数,无需写参数名,直接用 it
表示该参数:
Kotlin
// 定义一个"将字符串转为大写"的 Lambda(接收1个String,返回String)
val toUppercase: (String) -> String = { it.uppercase() }
// 调用
println(toUppercase("kotlin")) // 输出:KOTLIN
规则 3:代码块只有一行时,省略 ->
和 {}
(仅在作为函数参数时可用)
当 Lambda 作为函数参数,且代码块只有一行时,可进一步简化:
Kotlin
// 定义一个接收 Lambda 的函数(参数为 String,返回 String)
fun processString(str: String, action: (String) -> String): String {
return action(str)
}
// 简化调用:省略 {} 和 ->,直接写代码块
val result = processString("hello") { it.uppercase() }
println(result) // 输出:HELLO
3. Lambda 的返回值
Lambda 无需显式 return
,代码块最后一行表达式的结果即为返回值。若有多行代码,最后一行决定返回值:
Kotlin
// Lambda:接收两个Int,返回较大值(多行代码)
val getMax: (Int, Int) -> Int = { a, b ->
println("比较 $a 和 $b") // 非最后一行,仅执行逻辑
if (a > b) a else b // 最后一行,作为返回值
}
println(getMax(4, 6)) // 输出:比较 4 和 6 → 6
五、高阶函数:参数或返回值为函数的函数
高阶函数 是 Kotlin 的核心特性之一,定义为:参数是函数,或返回值是函数的函数。它的本质是 "用函数作为参数 / 返回值",结合 Lambda 可大幅简化代码。
1. 高阶函数的定义与调用
语法:参数或返回值的类型用 "(参数类型) -> 返回值类型
" 表示(即 Lambda 类型)。
示例 1:参数是函数(最常用场景)
定义一个 "对两个数执行自定义操作" 的高阶函数:
Kotlin
// 高阶函数:参数1(a: Int)、参数2(b: Int)、参数3(action: (Int, Int) -> Int)
fun operate(a: Int, b: Int, action: (Int, Int) -> Int): Int {
return action(a, b) // 调用传入的函数(Lambda)
}
// 调用高阶函数:传入 Lambda 作为参数
val addResult = operate(3, 5) { a, b -> a + b } // 加法
val multiplyResult = operate(3, 5) { a, b -> a * b } // 乘法
println(addResult) // 输出:8
println(multiplyResult) // 输出:15
示例 2:返回值是函数
定义一个 "根据操作符返回对应计算函数" 的高阶函数:
Kotlin
// 高阶函数:返回值类型是 (Int, Int) -> Int(函数类型)
fun getCalculator(operator: String): (Int, Int) -> Int {
return when (operator) {
"+" -> { a, b -> a + b } // 返回加法函数
"-" -> { a, b -> a - b } // 返回减法函数
"*" -> { a, b -> a * b } // 返回乘法函数
else -> { a, b -> 0 } // 默认返回0
}
}
// 调用高阶函数,获取加法函数
val addCalculator = getCalculator("+")
println(addCalculator(2, 3)) // 输出:5
// 直接调用返回的函数
println(getCalculator("*")(4, 5)) // 输出:20(先获取乘法函数,再调用)
2. Kotlin 常用高阶函数
Kotlin 标准库提供了 let
、run
、apply
、also
、with
等常用高阶函数,它们都是扩展函数(给任意对象添加方法),用于简化对象操作。
(1)let:非空判断 + 对象操作,返回最后一行结果
- 作用:对非空对象执行操作,避免空指针(
obj?.let
); - 内部用
it
指代当前对象; - 返回值:Lambda 最后一行的结果。
示例:处理可能为 null 的字符串:
Kotlin
val str: String? = "Kotlin"
// 若 str 非空,执行 let 内部逻辑(it 即 str)
str?.let {
println("字符串长度:${it.length}") // 输出:字符串长度:6
it.uppercase() // 最后一行作为返回值
}?.let {
println("大写后:$it") // 输出:大写后:KOTLIN
}
// 若 str 为 null,let 内部逻辑不执行
val nullStr: String? = null
nullStr?.let {
println(it.length) // 不执行
}
(2)run:对象成员访问 + 结果返回,内部用 this
- 作用:访问对象的成员(属性 / 方法),无需重复写对象名;
- 内部用
this
指代当前对象(可省略); - 返回值:Lambda 最后一行的结果。
示例:操作对象的成员:
Kotlin
data class Person(var name: String, var age: Int)
val person = Person("Alice", 25)
// 用 run 访问 person 的成员(this 即 person)
val newAge = person.run {
name = "Bob" // 等价于 person.name = "Bob"
age += 1 // 等价于 person.age += 1
println("新名字:$name,当前年龄:$age") // 输出:新名字:Bob,当前年龄:26
age // 最后一行作为返回值
}
println(newAge) // 输出:26
(3)apply:对象初始化 / 配置,返回对象本身
- 作用:初始化对象(设置属性),或对对象进行配置;
- 内部用
this
指代当前对象(可省略); - 返回值:当前对象本身(而非 Lambda 结果)。
示例:初始化集合:
Kotlin
// 用 apply 初始化 mutableList(返回 list 本身)
val list = mutableListOf<Int>().apply {
add(1)
add(2)
add(3)
removeAt(0) // 删除第一个元素(现在是 [2,3])
}
println(list) // 输出:[2, 3]
(4)also:对象额外操作(如日志),返回对象本身
- 作用:对对象执行额外操作(如打印日志、记录状态);
- 内部用
it
指代当前对象; - 返回值:当前对象本身。
示例:记录对象操作过程:
Kotlin
val num = 10.also {
println("原始值:$it") // 输出:原始值:10
}
.plus(5) // 10+5=15,返回15
.also {
println("加5后:$it") // 输出:加5后:15
}
.plus(3) // 15+3=18
println(num) // 输出:18
(5)with:非扩展函数,直接操作对象
- 作用:类似
run
,但不是扩展函数,需显式传入对象; - 内部用
this
指代传入的对象; - 返回值:Lambda 最后一行的结果。
示例:操作对象成员:
Kotlin
data class Book(var title: String, var author: String)
val book = Book("Kotlin 实战", "张三")
// with(对象) { ... },内部 this 即 book
val bookInfo = with(book) {
title = "Kotlin 实战(第2版)" // 修改属性
"${title} - 作者:$author" // 最后一行作为返回值
}
println(bookInfo) // 输出:Kotlin 实战(第2版) - 作者:张三
3. 高阶函数的应用场景
(1)简化代码:替代繁琐的判断 / 循环
比如用 let
替代 if (obj != null)
的非空判断:
Kotlin
// 传统写法:多次判断非空
val str: String? = "hello"
if (str != null) {
val upperStr = str.uppercase()
if (upperStr.length > 3) {
println(upperStr)
}
}
// 简化写法:用 let 链式调用
str?.uppercase()?.let {
if (it.length > 3) println(it)
}
(2)实现回调:替代匿名内部类
在 Android 或其他框架中,常用 Lambda 作为回调(如按钮点击、网络请求回调):
Kotlin
// Android 中按钮点击回调(传统匿名内部类)
btn.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View?) {
Toast.makeText(context, "点击了按钮", Toast.LENGTH_SHORT).show()
}
})
// 简化写法:用 Lambda 作为回调
btn.setOnClickListener {
Toast.makeText(context, "点击了按钮", Toast.LENGTH_SHORT).show()
}