rokevin
移动
前端
语言
  • 基础

    • Linux
    • 实施
    • 版本构建
  • 应用

    • WEB服务器
    • 数据库
  • 资讯

    • 工具
    • 部署
开放平台
产品设计
  • 人工智能
  • 云计算
计算机
其它
GitHub
移动
前端
语言
  • 基础

    • Linux
    • 实施
    • 版本构建
  • 应用

    • WEB服务器
    • 数据库
  • 资讯

    • 工具
    • 部署
开放平台
产品设计
  • 人工智能
  • 云计算
计算机
其它
GitHub
  • 函数

  • Kotlin 核心函数类型全览
  • 核心函数类型速查表
  • 核心使用原则
  • Kotlin 中如何定义函数?有哪些特殊的函数类型?
    • Kotlin 中的扩展函数是什么?如何使用?
    • Kotlin 中的高阶函数是什么?如何使用?
  • 除了内联函数,Kotlin 还有哪些优化高阶函数的方式?
    • 1. 复用函数对象(避免重复创建闭包)
      • 1.1 使用 val 存储函数引用(静态复用)
      • 1.2. 使用 lazy 延迟初始化函数对象
    • 2. 使用函数引用替代 lambda(减少闭包)
    • 3. 使用 fun interface(函数式接口)替代普通函数类型
      • 示例:自定义函数式接口
    • 4. 避免闭包捕获外部变量(减少对象持有)
      • 反例(捕获外部变量):
      • 优化(无状态 lambda / 函数):
    • 5. 使用 Sequence 替代 Collection 操作(减少中间闭包)
      • 反例(集合操作,多轮闭包):
      • 优化(Sequence 惰性求值,少闭包、少临时集合):

函数

Kotlin 核心函数类型全览

Kotlin 的函数类型可从不同维度划分,核心类型包括:

  1. 按「调用语法」:中缀函数(Infix Function)
  2. 按「归属 / 扩展能力」:扩展函数(Extension Function)、成员函数、顶层函数
  3. 按「编译方式」:内联函数(Inline Function)、非内联函数
  4. 按「参数 / 返回值」:高阶函数(Higher-Order Function)、普通函数
  5. 按「执行特性」:挂起函数(Suspend Function)、普通函数
  6. 按「可见性 / 作用域」:局部函数、成员函数、顶层函数
  7. 其他特殊函数:操作符重载函数、尾递归函数

核心函数类型速查表

函数类型核心关键字核心特征典型使用场景
中缀函数infix成员 / 扩展函数、单个参数、中缀调用语法二元关系操作(如关联、运算、判断)
扩展函数无为已有类添加方法,不修改源码第三方类扩展、工具方法面向对象化
内联函数inline函数体内联到调用处,减少闭包开销高阶函数优化、频繁调用的小函数
高阶函数无接收 / 返回函数类型函数式编程、通用逻辑提取、回调函数
挂起函数suspend协程中可暂停 / 恢复,非阻塞异步操作(网络、数据库、文件)、协程开发
局部函数无函数内部定义,仅内部可用封装函数内重复逻辑
操作符重载函数operator重载运算符,支持自定义类的运算符操作自定义类的运算符简化(如 Point 的 +、-)
尾递归函数tailrec尾递归优化为循环,避免栈溢出深度递归场景(阶乘、树形遍历)

核心使用原则

  1. 需简化二元关系调用→中缀函数;
  2. 需扩展已有类功能→扩展函数;
  3. 需优化高阶函数性能→内联函数;
  4. 需函数作为参数 / 返回值→高阶函数;
  5. 需异步非阻塞操作→挂起函数;
  6. 需递归优化→尾递归函数;
  7. 需自定义运算符→操作符重载函数。

Kotlin 中如何定义函数?有哪些特殊的函数类型?

Kotlin 中使用 fun 关键字定义函数,基本语法是:fun 函数名(参数列表): 返回类型 { 函数体 }。相比 Java,Kotlin 的函数定义更灵活、功能更强大。

// 基本函数定义
fun add(a: Int, b: Int): Int {
    return a + b
}

// 单表达式函数,可以省略花括号和 return
fun add2(a: Int, b: Int) = a + b

// 无返回值的函数,返回类型是 Unit(可省略)
fun printMessage(msg: String) {
    println(msg)
}

// 默认参数
fun greet(name: String, prefix: String = "你好") {
    println("$prefix,$name")
}

// 命名参数
greet(name = "张三", prefix = "早上好")
greet(name = "李四")  // 使用默认参数

Kotlin 还有一些特殊的函数类型。扩展函数可以为已有的类添加新方法,而不需要继承或修改原类。中缀函数允许用中缀表示法调用,让代码更自然。内联函数能减少 Lambda 的性能开销。高阶函数可以接收函数作为参数或返回函数。

// 扩展函数
fun String.addPrefix(prefix: String) = "$prefix$this"
val result = "世界".addPrefix("你好,")  // "你好,世界"

// 中缀函数
infix fun Int.times(str: String) = str.repeat(this)
val result2 = 3 times "嗨"  // "嗨嗨嗨"

// 高阶函数
fun calculate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
    return operation(a, b)
}
val sum = calculate(5, 3) { x, y -> x + y }

  1. 默认参数和命名参数的优势

默认参数和命名参数是 Kotlin 的贴心设计。在 Java 中,如果要支持多种参数组合,需要写多个重载方法,代码很冗余。Kotlin 的默认参数一个函数就能搞定。命名参数让函数调用更清晰,特别是参数很多时,不容易搞混参数顺序。

  1. 扩展函数的强大之处

扩展函数是 Kotlin 最实用的特性之一。你可以为任何类添加方法,包括 Java 标准库的类。比如给 String 加个判空方法、给 List 加个自定义排序方法。这种能力让代码更优雅,不用写一堆工具类。需要注意的是,扩展函数是静态解析的,不能真正修改原类,也不能访问 private 成员。

  1. 内联函数的性能优化

使用高阶函数和 Lambda 表达式会创建额外的对象,有一定性能开销。内联函数(inline)能让编译器把函数体直接插入到调用处,避免函数调用和对象创建的开销。Kotlin 标准库中的很多高阶函数都是内联的,比如 let、apply、also 等。如果你自己写高阶函数,也可以考虑用 inline 优化。

inline fun <T> measureTime(block: () -> T): T {
    val start = System.currentTimeMillis()
    val result = block()
    val end = System.currentTimeMillis()
    println("耗时:${end - start}ms")
    return result
}
  1. 顶层函数简化工具类

Kotlin 允许在文件顶层直接定义函数,不需要放在类里面。这特别适合写工具方法,不用像 Java 那样创建工具类、定义静态方法。顶层函数编译后会变成静态方法,性能没有损失。这种设计让代码结构更清晰,避免了 Java 中到处都是 XxxUtil 类的情况。

Kotlin 中的扩展函数是什么?如何使用?

扩展函数是 Kotlin 最实用的特性之一,它允许你为已有的类添加新方法,而不需要继承这个类或者修改它的源代码。简单来说,就是给别人的类"打补丁",加上你需要的功能。

扩展函数的定义语法是:fun 接收者类型.函数名(参数): 返回类型。在函数体内,可以用 this 访问接收者对象,通常 this 可以省略。

// 给 String 类添加一个扩展函数
fun String.addSuffix(suffix: String): String {
    return this + suffix  // this 指向调用对象
}

// 使用扩展函数
val result = "你好".addSuffix(",世界")
println(result)  // 输出:你好,世界

// 给 Int 添加扩展函数,判断是否为偶数
fun Int.isEven(): Boolean = this % 2 == 0

println(10.isEven())  // true
println(7.isEven())   // false

// 给 List 添加扩展函数,获取第二个元素
fun <T> List<T>.secondOrNull(): T? {
    return if (this.size >= 2) this[1] else null
}

val list = listOf("a", "b", "c")
println(list.secondOrNull())  // b

需要注意的是,扩展函数是静态解析的,不是真正修改了原类。它们不能访问类的 private 成员,本质上是编译器的语法糖,最终编译成静态方法。

  1. 扩展函数的实现原理

扩展函数看起来像类的成员方法,但实际上是静态方法。编译器会把接收者对象作为第一个参数传给这个静态方法。比如 "abc".addSuffix("def") 会被编译成类似 StringExtKt.addSuffix("abc", "def") 的调用。理解这个原理,就能明白为什么扩展函数不能访问 private 成员,因为它不是真正的成员方法。

  1. 扩展函数和成员函数的优先级

如果扩展函数和成员函数签名相同,成员函数的优先级更高。编译器会优先调用成员函数,扩展函数会被忽略。这是合理的设计,保证了类的原有行为不会被扩展函数意外覆盖。

class MyClass {
    fun foo() { println("成员函数") }
}

fun MyClass.foo() { println("扩展函数") }

MyClass().foo()  // 输出:成员函数
  1. 可空类型的扩展

扩展函数还可以定义在可空类型上,这样即使对象为 null,也可以调用这个方法。这在处理可能为 null 的对象时特别有用。

fun String?.isNullOrEmpty2(): Boolean {
    return this == null || this.isEmpty()
}

val str: String? = null
println(str.isNullOrEmpty2())  // true,不会抛异常
  1. 扩展函数的实际应用

在 Android 开发中,扩展函数被大量使用。比如给 View 添加点击防抖的扩展函数、给 Context 添加显示 Toast 的扩展函数、给 String 添加格式化的扩展函数等。Kotlin 标准库也提供了大量扩展函数,比如 let、apply、also 等作用域函数,还有集合的 map、filter 等操作。善用扩展函数能让代码更简洁、更符合直觉。建议把常用的工具方法都写成扩展函数,而不是传统的静态工具类。

Kotlin 中的高阶函数是什么?如何使用?

高阶函数是指接收函数作为参数,或者返回函数的函数。在 Kotlin 中,函数是一等公民,可以像其他类型一样作为参数传递和返回,这让高阶函数的使用非常自然。

// 接收函数作为参数的高阶函数
fun calculate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
    return operation(a, b)
}

// 使用高阶函数
val sum = calculate(5, 3) { x, y -> x + y }
val product = calculate(5, 3) { x, y -> x * y }
println(sum)      // 8
println(product)  // 15

// 返回函数的高阶函数
fun makeMultiplier(factor: Int): (Int) -> Int {
    return { num -> num * factor }
}

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

高阶函数让代码更灵活、更简洁。Kotlin 标准库中大量使用高阶函数,比如集合的 map、filter、fold 等操作,都是高阶函数。掌握高阶函数是写出优雅 Kotlin 代码的关键。

  1. 函数类型的声明

在 Kotlin 中,函数类型用圆括号包含参数类型,箭头指向返回类型。比如 (Int, Int) -> Int 表示接收两个 Int 参数,返回 Int 的函数。函数类型本身也是一种类型,可以用于变量声明、参数类型、返回类型等。

// 函数类型的变量
val sum: (Int, Int) -> Int = { a, b -> a + b }

// 无参数的函数类型
val sayHello: () -> Unit = { println("Hello") }

// 接收函数类型的函数类型(高阶的高阶函数)
val higherOrder: ((Int) -> Int) -> Int = { func -> func(10) }
  1. Lambda 表达式的简化

Kotlin 的 Lambda 表达式有很多简化写法。如果 Lambda 是函数的最后一个参数,可以放在括号外面。如果 Lambda 是唯一的参数,括号可以省略。如果 Lambda 只有一个参数,可以用 it 代替。这些简化让代码非常简洁。

// 完整写法
list.filter({ item -> item > 5 })

// Lambda 在括号外
list.filter() { item -> item > 5 }

// 省略空括号
list.filter { item -> item > 5 }

// 使用 it
list.filter { it > 5 }
  1. 内联函数优化性能

使用高阶函数会创建 Lambda 对象,有一定性能开销。Kotlin 提供了 inline 关键字,让编译器把函数体和 Lambda 直接内联到调用处,避免对象创建。标准库的很多高阶函数都是内联的,所以性能不用担心。

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

// 调用时,代码会被内联,没有额外开销
measureTime {
    // 一些耗时操作
}
  1. 高阶函数在实际开发中的应用

在 Android 开发中,高阶函数被大量使用。比如事件监听(OnClickListener 可以用 Lambda 简化)、协程的 launch 和 async、Jetpack Compose 的声明式 UI、数据流转换等。掌握高阶函数能让代码更简洁、更函数式。很多框架的 DSL 风格 API 都是基于高阶函数实现的。

// 简化的点击监听
button.setOnClickListener { view ->
    println("点击了")
}

// 协程
launch {
    val result = async { fetchData() }
    updateUI(result.await())
}

// Compose UI
Column {
    Text("标题")
    Button(onClick = { /* 点击事件 */ }) {
        Text("按钮")
    }
}

除了内联函数,Kotlin 还有哪些优化高阶函数的方式?

除了 inline 内联函数,Kotlin 优化高阶函数的核心方式可归纳为 3 类:

  1. 减少闭包创建:复用函数对象(val 存储、lazy)、使用函数引用、fun interface;
  2. 降低闭包开销:避免捕获外部变量,让 lambda 无状态;
  3. 减少中间操作:用 Sequence 惰性求值替代集合的急切操作,减少闭包和临时对象。

实际开发中,建议优先根据场景选择:

  • 高频、简单的高阶函数 → inline;
  • 需传递 / 复用的函数对象 → 函数引用 + val 存储;
  • 集合链式操作 → Sequence;
  • 跨语言 / 自定义逻辑 → fun interface。

1. 复用函数对象(避免重复创建闭包)

核心思路:既然闭包对象的创建是性能瓶颈,那复用同一个函数对象就能大幅减少对象创建和 GC 压力。

1.1 使用 val 存储函数引用(静态复用)

将高频使用的 lambda / 函数类型对象赋值给顶层 / 成员 val,每次调用高阶函数时复用这个对象,而不是每次都新建。

// 定义一个复用的函数对象(仅创建一次)
private val ADD_OPERATION: (Int, Int) -> Int = { a, b -> a + b }

// 普通高阶函数(非 inline)
fun calculate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
    return operation(a, b)
}

// 调用处(高频循环)
fun main() {
    // 循环 1000 次,复用同一个 ADD_OPERATION 对象,仅创建 1 次闭包
    repeat(1000) {
        val result = calculate(10, 20, ADD_OPERATION)
        println(result)
    }
}

适用场景:lambda 逻辑固定、无捕获外部变量(无状态)的高频调用场景。

1.2. 使用 lazy 延迟初始化函数对象

如果函数对象依赖上下文(需捕获变量),但仍想复用,可结合 lazy 延迟创建且只创建一次:

class Calculator(private val base: Int) {
    // 延迟创建,且仅创建一次(捕获了 base 变量)
    private val multiplyOperation by lazy { (num: Int) -> num * base }

    fun compute(num: Int): Int {
        // 每次调用都复用同一个 multiplyOperation 对象
        return process(num, multiplyOperation)
    }

    private fun process(num: Int, op: (Int) -> Int): Int {
        return op(num)
    }
}

2. 使用函数引用替代 lambda(减少闭包)

核心思路:Kotlin 中 ::函数名 形式的函数引用,编译时会复用预定义的函数对象(而非每次创建新闭包),比匿名 lambda 更高效。

// 定义普通函数
fun add(a: Int, b: Int): Int {
    return a + b
}

// 高阶函数
fun calculate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
    return operation(a, b)
}

fun main() {
    // 方式1:匿名 lambda(每次创建新闭包)
    repeat(1000) {
        calculate(10, 20) { a, b -> a + b }
    }

    // 方式2:函数引用(复用预定义对象,无新闭包创建)
    repeat(1000) {
        calculate(10, 20, ::add)
    }
}

关键区别:

  • 匿名 lambda:每次调用都会生成新的 Function2 实例;
  • 函数引用:JVM 会缓存这个函数引用对应的对象,全程复用。

3. 使用 fun interface(函数式接口)替代普通函数类型

核心思路:Kotlin 1.4+ 引入的 fun interface(单抽象方法接口),比普通的 (T) -> R 函数类型更轻量,且支持 SAM 转换,同时能避免一些隐式的闭包开销。

示例:自定义函数式接口

// 定义函数式接口(仅一个抽象方法)
fun interface Operation {
    fun execute(a: Int, b: Int): Int
}

// 高阶函数接收函数式接口参数
fun calculate(a: Int, b: Int, operation: Operation): Int {
    return operation.execute(a, b)
}

fun main() {
    // SAM 转换:自动创建接口实现类(仅创建一次,若复用则无额外开销)
    val addOperation = Operation { a, b -> a + b }
    
    // 高频调用,复用同一个接口实例
    repeat(1000) {
        calculate(10, 20, addOperation)
    }
}

优势:

  • 比普通函数类型更灵活(可定义默认方法、属性);
  • 编译时生成的字节码更简洁,调用开销略低于通用的 FunctionN 类型;
  • 支持 Java 互操作(Java 中可直接用 lambda 实现,无额外适配)。

4. 避免闭包捕获外部变量(减少对象持有)

核心思路:如果 lambda 捕获了外部变量,闭包对象会持有这些变量的引用(增加内存占用,甚至引发泄漏),尽量让 lambda 无状态(不捕获外部变量)能大幅优化。

反例(捕获外部变量):

fun badExample() {
    val base = 10 // 外部变量
    // lambda 捕获 base,闭包对象会持有 base 的引用
    val operation = { num: Int -> num + base }
    repeat(1000) { calculate(it, 20, operation) }
}

优化(无状态 lambda / 函数):

// 无状态函数,不捕获任何外部变量
fun addWithBase(num: Int, base: Int): Int {
    return num + base
}

fun goodExample() {
    val base = 10
    // 把外部变量作为参数传入,而非让 lambda 捕获
    repeat(1000) { calculate(it, 20) { a, b -> addWithBase(a, base) } }
}

关键:捕获的变量越多,闭包对象越大,GC 压力也越大;无状态的 lambda / 函数是性能最优的。

5. 使用 Sequence 替代 Collection 操作(减少中间闭包)

核心思路:Kotlin 集合的 map/filter 等高阶函数是急切求值的(每一步都生成新集合),而 Sequence 是惰性求值的(仅在最终消费时执行),能大幅减少中间闭包和临时集合的创建。

反例(集合操作,多轮闭包):

fun collectionExample() {
    val list = (1..10000).toList()
    // 每一步都创建新集合 + 新闭包,性能差
    val result = list.filter { it % 2 == 0 }
                     .map { it * 10 }
                     .sum()
}

优化(Sequence 惰性求值,少闭包、少临时集合):

fun sequenceExample() {
    val list = (1..10000).toList()
    // 仅在 sum() 时执行,全程无中间集合,闭包复用
    val result = list.asSequence()
                     .filter { it % 2 == 0 }
                     .map { it * 10 }
                     .sum()
}

核心优势:

  • 集合操作:filter → 生成新列表 → map → 生成新列表(两次闭包、两次集合创建);
  • Sequence 操作:filter 和 map 的 lambda 仅在遍历时分次执行,无中间集合,闭包仅创建一次。
最近更新:: 2026/4/2 20:07
Contributors: luokaiwen