0
点赞
收藏
分享

微信扫一扫

kotlin 常用的几种函数

好的,Kotlin 以其简洁、安全和强大的函数式编程特性而闻名。以下是 Kotlin 中最常用和最具特色的几种函数类型及其用法。

Kotlin 常用函数类型详解

一、 普通函数 (Regular Functions)

这是最基础的函数定义方式。

// 基本语法
fun functionName(param1: Type, param2: Type): ReturnType {
    // 函数体
    return value
}

// 示例
fun add(a: Int, b: Int): Int {
    return a + b
}

// 单表达式函数 (Single-expression function)
// 当函数体只有一个表达式时,可以用 `=` 简化
fun add(a: Int, b: Int): Int = a + b

// 返回类型可以省略,由编译器推断
fun add(a: Int, b: Int) = a + b

// 无返回值函数 (返回 Unit)
fun printSum(a: Int, b: Int) {
    println("sum is ${a + b}")
}
// 等价于
fun printSum(a: Int, b: Int): Unit {
    println("sum is ${a + b}")
}

二、 扩展函数 (Extension Functions)

核心概念: 允许你为一个类(可以是系统类、第三方库类或你自己定义的类)添加新的函数,而无需修改该类的源码或使用继承。

  • 语法:

fun ReceiverType.functionName(params): ReturnType {
    // 函数体
    // 在函数体内,`this` 指向 ReceiverType 的实例
}

  • 示例:

// 为 String 类添加一个扩展函数
fun String.lastChar(): Char = this.get(this.length - 1)
// 或者更简洁
fun String.lastChar(): Char = this[this.length - 1]

// 使用
val str = "Hello"
println(str.lastChar()) // 输出 'o'

// 为 Int 添加平方函数
fun Int.square(): Int = this * this
println(5.square()) // 输出 25

  • 关键点:
  • 扩展函数不能访问类的 privateprotected 成员。
  • 扩展函数是静态解析的。调用哪个函数在编译时就确定了,基于变量声明的类型,而不是运行时的实际类型(与成员函数的动态分发不同)。
  • 命名冲突: 如果类本身有一个同名同参的成员函数,成员函数优先级更高。
  • 应用场景:
  • 为系统类(String, Int, List 等)添加便捷方法。
  • 在工具类中组织相关功能,使其看起来像对象的原生方法。
三、 高阶函数 (Higher-Order Functions)

核心概念: 函数可以作为参数传递给另一个函数,或者函数可以作为返回值被另一个函数返回。

  • 函数类型 (Function Types):
  • () -> Unit: 接受 0 个参数,返回 Unit (无意义) 的函数。
  • (Int) -> String: 接受 1 个 Int 参数,返回 String 的函数。
  • (String, Int) -> Boolean: 接受 StringInt 参数,返回 Boolean 的函数。
  • (Int, Int) -> Int: 二元操作函数。
  • 作为参数:

// 定义一个高阶函数:接受一个函数作为参数
fun operate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
    return operation(a, b)
}

// 使用
val sum = operate(3, 4) { x, y -> x + y } // 传递 lambda
val product = operate(3, 4, ::multiply)   // 传递函数引用

fun multiply(x: Int, y: Int) = x * y

  • 作为返回值:

// 返回一个函数
fun getOperation(operation: String): (Int, Int) -> Int {
    return when (operation) {
        "add" -> { a, b -> a + b }
        "multiply" -> { a, b -> a * b }
        else -> { a, b -> a - b }
    }
}

val op = getOperation("add")
println(op(5, 3)) // 输出 8

  • 内联函数 (inline functions):
  • 使用 inline 关键字修饰高阶函数。
  • 目的: 消除函数调用的开销(尤其是 lambda 的开销)。编译器会将函数体内联到调用处。
  • 适用: 高频调用的高阶函数(如 let, apply, run, with, also, forEach 等标准库函数都是 inline 的)。
  • 注意: 内联会增加生成的字节码大小。

inline fun myInlineFunction(block: () -> Unit) {
    // 函数体
    block()
}

  • 应用场景:
  • 标准库中的集合操作:filter, map, flatMap, reduce, fold 等。
  • 资源管理:use 函数确保资源被正确关闭。
  • 实现 DSL (领域特定语言)。
四、 Lambda 表达式 (Lambda Expressions)

Lambda 是定义匿名函数的简洁语法。它是实现函数类型最常用的方式。

  • 基本语法:

{ param1: Type, param2: Type -> body }

  • 简化规则:
  1. 参数类型可省略(编译器可推断)。
  2. 单个参数时,参数名可省略,默认为 it
  3. 如果 lambda 是函数调用的最后一个参数,可以移到括号外
  4. 如果 lambda 是唯一参数,括号可以省略
  • 示例:

val numbers = listOf(1, 2, 3, 4, 5)

// 带参数名和类型的完整形式
val doubled1 = numbers.map({ number: Int -> number * 2 })

// 省略类型
val doubled2 = numbers.map({ number -> number * 2 })

// 参数移到括号外
val doubled3 = numbers.map() { number -> number * 2 }

// 省略空括号
val doubled4 = numbers.map { number -> number * 2 }

// 单个参数,使用 it
val doubled5 = numbers.map { it * 2 }

// 过滤偶数
val evens = numbers.filter { it % 2 == 0 }

  • 函数引用 (Function References):
  • 使用 :: 操作符将现有函数作为 lambda 传递。

fun isEven(n: Int) = n % 2 == 0
val evens = numbers.filter(::isEven) // 等价于 numbers.filter { isEven(it) }

五、 带接收者的函数字面量 (Function Literals with Receiver)

也称为带接收者的 Lambda。它允许你在 lambda 内部像调用成员函数一样调用接收者对象的方法。

  • 语法:

val lambda: ReceiverType.() -> ReturnType = { /* this 指向 ReceiverType 的实例 */ }

  • 标准库中的典型应用 (let, apply, run, with, also):

data class Person(var name: String, var age: Int)

val person = Person("Alice", 25)

// let: 将对象作为 lambda 的参数 (it)
val description = person.let { 
    "Name: ${it.name}, Age: ${it.age}" 
}

// run: 在对象的上下文中执行代码,返回最后一行
val updatedPerson = person.run {
    name = "Bob"
    age = 30
    this // 返回对象本身 (可省略)
}

// with: 类似 run,但写法不同 (不是扩展函数)
val result = with(person) {
    name = "Charlie"
    age = 35
    "Updated"
}

// apply: 在对象的上下文中配置,**返回对象本身**
val configuredPerson = person.apply {
    name = "Diana"
    age = 28
} // configuredPerson 仍然是 person 对象

// also: 类似 apply,但 lambda 的接收者是对象,常用于附加操作
val anotherPerson = person.also {
    println("Configuring ${it.name}")
    it.name = "Eve"
} // anotherPerson 仍然是 person 对象

  • 应用场景:
  • 对象配置: apply 是初始化或配置对象属性的绝佳选择。
  • 作用域函数: let, run, also 用于在特定作用域内操作对象,并控制返回值。
  • 构建 DSL: 是创建流畅 API 的基础。
六、 局部函数 (Local Functions)

在另一个函数内部定义的函数。

fun validateAndProcess(name: String, age: Int) {
    // 局部函数
    fun validateName() {
        if (name.isBlank()) throw IllegalArgumentException("Name cannot be blank")
    }

    fun validateAge() {
        if (age < 0) throw IllegalArgumentException("Age cannot be negative")
    }

    // 调用局部函数
    validateName()
    validateAge()

    // 处理逻辑
    println("Processing $name, age $age")
}

  • 优点:
  • 封装逻辑,避免代码重复。
  • 可以访问外部函数的局部变量(闭包)。
七、 尾递归函数 (Tail Recursive Functions)

使用 tailrec 修饰符,编译器会将其优化为循环,避免栈溢出。

// 普通递归 (可能栈溢出)
fun factorial(n: Long): Long {
    return if (n <= 1) 1 else n * factorial(n - 1)
}

// 尾递归 (编译器优化为循环)
tailrec fun factorialTail(n: Long, accumulator: Long = 1): Long {
    return if (n <= 1) accumulator else factorialTail(n - 1, n * accumulator)
}

  • 要求: 递归调用必须是函数的最后一个操作

总结对比

函数类型

核心特点

主要用途

普通函数

基础函数定义

所有场景的基础

扩展函数

为现有类添加新函数

增强类的功能,创建 DSL

高阶函数

函数作为参数或返回值

实现函数式编程,集合操作,资源管理

Lambda 表达式

匿名函数的简洁语法

实现高阶函数的参数,简化代码

带接收者的 Lambda

在对象上下文中执行代码

apply, run, let 等作用域函数,对象配置

局部函数

函数内部的函数

封装私有逻辑,避免重复

尾递归函数

递归调用在末尾,可优化

安全的递归算法

掌握这些函数类型,特别是扩展函数、高阶函数、Lambda 和作用域函数 (let, apply, run, with, also),是写出地道、简洁、高效的 Kotlin 代码的关键。

举报

相关推荐

0 条评论