Kotlin学习第 4 课:Kotlin 函数:从基础定义到高阶应用

函数是 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): Intfun 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,所以 ab 的类型可省略。

规则 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 标准库提供了 letrunapplyalsowith 等常用高阶函数,它们都是扩展函数(给任意对象添加方法),用于简化对象操作。

(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()
}
相关推荐
mg6684 小时前
安卓玩机工具----安卓“搞机工具箱”最新版 控制手机的玩机工具
android·智能手机
诺诺Okami4 小时前
Android Framework- Activity启动2
android
渣哥4 小时前
使用 HashMap 提高性能的小技巧
java
米豆同学4 小时前
SystemUI plugin 开发
android
kyle~4 小时前
排序---快速排序(Quick Sort)
java·开发语言
小蒜学长4 小时前
旅行社旅游管理系统的设计与实现(代码+数据库+LW)
java·数据库·spring boot·后端·旅游
Kevinyu_4 小时前
RabbitMQ
java·rabbitmq·java-rabbitmq