Kotlin 函数(Function)全面解析
函数是 Kotlin 中最核心的代码复用和逻辑封装单元,相比 Java 提供了更灵活、简洁、强大的语法特性。本文从基础到进阶,结合代码示例详细讲解函数的核心知识点,并附上官方权威资料链接。
一、函数基础
1. 函数定义语法
Kotlin 函数使用 fun 关键字声明,基本结构如下:
// 修饰符 fun 函数名(参数名: 参数类型): 返回值类型 { 函数体 }
fun add(a: Int, b: Int): Int {
return a + b
}
- 修饰符(可选):如 public(默认)、private、internal、protected、inline 等;
- 参数:格式为 参数名: 类型,支持默认值、命名参数;
- 返回值:无返回值时可写 Unit(可省略),相当于 Java 的 void。
2. 无返回值函数
// 方式1:显式声明返回 Unit
fun printHello(name: String): Unit {
println("Hello, $name!")
}
// 方式2:省略 Unit(推荐)
fun printWelcome(name: String) {
println("Welcome, $name!")
}
3. 单表达式函数
如果函数体只有一个表达式,可以省略花括号和 return,直接用 = 连接:
// 等价于上面的 add 函数,更简洁
fun add(a: Int, b: Int): Int = a + b
// 返回值类型可自动推导
fun multiply(a: Int, b: Int) = a * b
4. 最简单的函数
fun sayHello() {
println("Hello!")
}
fun main() {
sayHello()
}
二、函数的参数
1. 传统位置参数调用
按参数定义顺序传递参数:
fun greet(name: String, age: Int) {
println("Name: $name, Age: $age")
}
fun main() {
greet("Alice", 25) // 顺序必须一致
}
2. 命名参数
调用时可以显式指定参数名,从而可以打乱顺序,提高可读性:
fun main() {
greet(age = 25, name = "Alice") // 使用命名参数,顺序可以调换
}
3. 默认参数
参数可以有默认值,调用时如果省略该参数,则使用默认值,避免 Java 中的重载冗余:
// 给 b 设置默认值 10
fun calculate(a: Int, b: Int = 10, c: Int = 5): Int {
return a + b * c
}
fun main() {
println(calculate(2)) // 2 + 10*5 = 52(使用 b、c 的默认值)
println(calculate(2, 3)) // 2 + 3*5 = 17(仅覆盖 b 的默认值)
println(calculate(2, 3, 4)) // 2 + 3*4 = 14(覆盖所有参数)
// 命名参数可以跳过中间的默认参数
println(calculate(a = 2, c = 8)) // 2 + 10*8 = 82
}
注意:如果默认参数位于非默认参数之前,只能通过命名参数调用才能省略前面的参数:
fun greet(greeting: String = "Hello", name: String) { // 默认参数在前
println("$greeting, $name")
}
fun main() {
greet(name = "Alice") // 必须使用命名参数,因为第一个参数被省略
}
4. 可变数量的参数(vararg)
使用 vararg 修饰符声明一个参数,表示可以接受可变数量的参数(类似 Java 的可变参数)。在函数内部,可变参数表现为对应类型的数组。
fun sum(vararg numbers: Int): Int {
var total = 0
for (num in numbers) {
total += num
}
return total
}
fun main() {
println(sum(1, 2, 3)) // 6
println(sum(10, 20, 30, 40)) // 100
}
展开操作符(Spread Operator)
当你已经有一个数组,想要将其内容传递给 vararg 参数时,可以使用 * 操作符展开数组:
fun main() {
val arr = intArrayOf(5, 6, 7)
println(sum(*arr)) // 18
// 可以混合普通参数和展开的数组
println(sum(1, *arr, 2)) // 1+5+6+7+2 = 21
}
三、函数的高级形式
1. 局部函数(嵌套函数)
Kotlin 支持在函数内部定义另一个函数,称为局部函数。局部函数可以访问外部函数的局部变量,常用于功能封装和避免代码重复。
fun validateUser(username: String, password: String): Boolean {
// 局部函数:校验非空
fun checkNotEmpty(str: String, msg: String) {
if (str.isEmpty()) {
throw IllegalArgumentException(msg)
}
}
checkNotEmpty(username, "用户名不能为空")
checkNotEmpty(password, "密码不能为空")
// 模拟校验逻辑
return username.length >= 6 && password.length >= 8
}
fun main() {
println(validateUser("kotlin123", "12345678")) // true
// validateUser("", "12345678") // 抛出异常
}
2. 中缀函数(infix)
用 infix 关键字修饰的函数,允许使用中缀表示法调用(即省略点号和括号),但必须满足以下条件:
- 必须是成员函数或扩展函数;
- 只有一个参数;
- 参数不能是可变参数且不能有默认值。
infix fun Int.add(other: Int): Int = this + other
fun main() {
val result = 3 add 5 // 中缀调用,等价于 3.add(5)
println(result) // 8
}
常见的中缀函数如 to(用于创建 Pair):1 to "one"。
3. 尾递归函数(tailrec)
当一个函数调用自身作为其最后一个操作(尾调用)时,可以使用 tailrec 修饰符,编译器会将递归优化为迭代,避免栈溢出。
tailrec fun factorial(n: Int, accumulator: Int = 1): Int {
return if (n <= 1) accumulator else factorial(n – 1, n * accumulator)
}
fun main() {
println(factorial(5)) // 120
}
注意:tailrec 要求递归调用必须是函数的最后一个操作,并且不能用在 try/catch/finally 块中。
4. 扩展函数
扩展函数可以在不继承类的情况下,为现有类添加新函数。扩展函数是静态解析的,并非真正修改类。
// 给 String 类扩展一个 "统计数字个数" 的函数
fun String.countDigits(): Int {
var count = 0
for (char in this) { // this 指代调用扩展函数的 String 实例
if (char.isDigit()) {
count++
}
}
return count
}
fun main() {
val str = "Kotlin12345Java678"
println(str.countDigits()) // 输出 8(12345678)
}
扩展函数可以访问接收者对象的公有成员,但不能访问私有或 protected 成员。
5. 泛型函数
函数可以声明类型参数,使其适用于多种类型。
fun <T> identity(value: T): T = value
fun main() {
val intResult = identity(42) // T 推断为 Int
val stringResult = identity("Kotlin") // T 推断为 String
println(intResult)
println(stringResult)
}
可以同时有多个类型参数,并对类型参数进行上界约束:
fun <T : Comparable<T>> max(a: T, b: T): T {
return if (a > b) a else b
}
fun main() {
println(max(10, 20)) // 20
println(max("A", "B")) // B
}
四、高阶函数与函数类型
1. 函数类型定义
函数类型格式:(参数类型1, 参数类型2) -> 返回值类型,例如:
- (Int, Int) -> Int:接收两个 Int,返回 Int 的函数;
- () -> Unit:无参数、无返回值的函数。
2. 高阶函数示例
高阶函数是指将函数作为参数或返回值的函数。
// 高阶函数:接收两个 Int 和一个函数类型参数
fun operate(a: Int, b: Int, op: (Int, Int) -> Int): Int {
return op(a, b) // 调用传入的函数
}
fun main() {
// 调用高阶函数:传入 lambda 表达式作为参数
val result1 = operate(10, 5) { x, y -> x + y } // 加法
val result2 = operate(10, 5) { x, y -> x * y } // 乘法
println(result1) // 15
println(result2) // 50
}
3. 内联函数(inline)
高阶函数通常会创建 lambda 的匿名类对象,带来一定的运行时开销。使用 inline 关键字修饰高阶函数,可以在调用处将函数体直接内联展开,避免创建匿名对象,提升性能。
// 内联高阶函数
inline fun logTime(block: () -> Unit) {
val start = System.currentTimeMillis()
block()
val end = System.currentTimeMillis()
println("执行耗时:${end – start}ms")
}
fun main() {
logTime {
Thread.sleep(100)
println("逻辑执行完成")
}
}
注意:inline 通常用于高阶函数,如果普通函数被标记为 inline,编译器也会内联,但通常没有性能收益,反而会增加字节码大小。
五、函数的作用域
1. 顶层函数
直接定义在文件中的函数,不属于任何类,可以从任意位置导入使用(类似 Java 的静态方法)。Kotlin 的顶层函数会被编译为对应文件名加 Kt 后缀的类的静态方法。
// File: Utils.kt
package com.example.utils
fun topLevelFunction() {
println("I'm a top-level function")
}
2. 成员函数
定义在类内部的函数,需要通过类的实例调用(或通过类名调用伴生对象中的函数)。
class MyClass {
fun memberFunction() {
println("Member function")
}
}
fun main() {
MyClass().memberFunction()
}
3. 局部函数
前面已介绍,定义在其他函数内部。
六、完整示例:综合运用
以下示例综合展示了多种函数特性:
// 顶层函数
fun main() {
println("计算结果:${calculate(10, 5)}")
printNumbers(1, 2, 3, 4, 5)
val greeting = "Kotlin"
println(greeting.addExclamation())
println(maxOf(10, 20))
// 高阶函数示例
logTime {
println("执行一些操作")
}
}
// 单表达式函数 + 默认参数
fun calculate(a: Int, b: Int, operation: String = "add") = when (operation) {
"add" -> a + b
"sub" -> a – b
else -> 0
}
// 可变参数 + 局部函数
fun printNumbers(vararg numbers: Int) {
fun printFormatted(num: Int) {
println("Number: $num")
}
for (n in numbers) printFormatted(n)
}
// 扩展函数
fun String.addExclamation() = "$this!"
// 泛型函数
fun <T : Comparable<T>> maxOf(a: T, b: T): T = if (a > b) a else b
// 内联高阶函数
inline fun logTime(block: () -> Unit) {
val start = System.currentTimeMillis()
block()
val end = System.currentTimeMillis()
println("耗时:${end – start}ms")
}
七、官方资料链接
总结
掌握这些函数特性,能够让你编写出更简洁、更灵活、更高效的 Kotlin 代码。

