Kotlin 扩展函数(Extension Functions) 允许你在不修改原类的情况下,为已有的类添加新的功能。扩展函数的本质是静态解析,即它们不会真正修改原类,而是通过 “类.函数” 的语法来调用。

1. 定义扩展函数

扩展函数的定义方式:

fun 类名.函数名(参数): 返回类型 {
    // 函数体
}

示例:为 String 添加一个扩展函数

fun String.firstChar(): Char {
    return this[0]
}
 
fun main() {
    val str = "Kotlin"
    println(str.firstChar()) // 输出: K
}
  • this 关键字指代调用该扩展函数的实例。
  • 这个扩展函数返回 String 的第一个字符。

2. 扩展函数的作用范围

2.1 作用于任何可访问的类

扩展函数可以定义在任何可访问的类上,比如 Java 标准库、Kotlin 自带的类等。

fun Int.square(): Int {
    return this * this
}
 
fun main() {
    println(5.square()) // 输出: 25
}

2.2 作用于自定义类

class Person(val name: String, val age: Int)
 
// 为 Person 添加扩展函数
fun Person.isAdult(): Boolean {
    return this.age >= 18
}
 
fun main() {
    val p = Person("Alice", 20)
    println(p.isAdult()) // 输出: true
}

3. 扩展函数 vs. 成员函数

如果一个类自身定义了某个函数,而扩展函数恰好有相同的名字,成员函数优先

class Example {
    fun printMessage() {
        println("成员函数")
    }
}
 
// 定义同名扩展函数
fun Example.printMessage() {
    println("扩展函数")
}
 
fun main() {
    val e = Example()
    e.printMessage() // 输出: 成员函数
}
  • 成员函数的优先级高于扩展函数。
  • 扩展函数不会覆盖原有的成员函数。

4. 伴生对象的扩展函数

Kotlin 允许为**伴生对象(companion object)**定义扩展函数:

class MyClass {
    companion object {}
}
 
// 扩展 MyClass 的 companion object
fun MyClass.Companion.greet() {
    println("Hello from companion object!")
}
 
fun main() {
    MyClass.greet() // 输出: Hello from companion object!
}

5. 作用于泛型类

扩展函数可以应用于泛型类型:

fun <T> List<T>.second(): T {
    if (this.size < 2) throw NoSuchElementException("List has fewer than 2 elements")
    return this[1]
}
 
fun main() {
    val numbers = listOf(1, 2, 3)
    println(numbers.second()) // 输出: 2
}

6. 扩展属性

Kotlin 还支持扩展属性(Extension Properties)

val String.lastChar: Char
get() = this[this.length - 1]
 
fun main() {
    val str = "Kotlin"
    println(str.lastChar) // 输出: n
}
  • 扩展属性不能有 field 存储值,因为它不会真正修改原类。
  • 只能通过 get()set() 进行访问。

7. 适用场景

  • 给 Java 类添加新方法(比如 Android 开发中对 TextView 添加方法)。
  • 提高代码可读性(让代码更加自然流畅)。
  • 避免继承(扩展函数可以减少对继承的依赖)。

8. 结论

Kotlin 的扩展函数提供了一种简洁优雅的方式来增强已有类的功能,而无需修改原代码。它适用于各种场景,如自定义工具函数、扩展标准库、提高代码复用性等,但要注意扩展函数不能覆盖成员函数,且仅在作用域内生效

如果你在实际开发中使用 Kotlin,一定会经常用到扩展函数! 🚀

9. 问题

扩展属性不能有 field 存储值,因为它不会真正修改原类。没理解这句话

这句话的意思是,Kotlin 的扩展属性不能真正地在类内部添加一个新的字段(field),因为扩展本质上并不会修改原类。扩展属性只能通过 getter(和 setter)从已有数据中计算或获取值,而不能存储值。

1. Kotlin 扩展属性的限制

在 Kotlin 中,普通类的属性通常有一个 field 关键字,用于存储实际的值:

class Person {
    var name: String = "Alice"
    get() = field  // 这里的 `field` 代表 name 这个变量存储的值
    set(value) {
        field = value
    }
}

这里 field 实际上存储了 name 的值,并且 get()set() 访问的是这个 field

但是,在扩展属性中,你不能声明 field,因为扩展本质上只是在外部“假装”给类添加了新属性,而不会修改类的结构。例如:

val String.firstChar: Char
get() = this[0] // 只能通过 getter 计算得到值

这个 firstChar 只是计算并返回 String 的第一个字符,但它并不会在 String 类内部新增一个 field 来存储这个值


2. 试图使用 field 的错误示例

如果你尝试在扩展属性中使用 field,Kotlin 会报错:

var String.newProperty: String
get() = field  // ❌ 编译错误:扩展属性不能有 backing field
set(value) {
    field = value
}

错误:扩展属性不能有 backing field

这说明扩展属性不能存储数据,它只能基于已有的数据计算或获取值。


3. 如何正确使用扩展属性

扩展属性可以通过 getter 访问已有数据,例如:

val String.lastChar: Char
get() = this[this.length - 1]
 
fun main() {
    println("Kotlin".lastChar) // 输出: n
}

或者,如果需要存储数据,必须借助 MutableMap 等外部结构

val nameMap = mutableMapOf<String, String>()
 
var String.nickname: String
get() = nameMap[this] ?: "Unknown"
set(value) {
    nameMap[this] = value
}
 
fun main() {
    val user = "Alice"
    user.nickname = "Lili" // 存入 map
    println(user.nickname)  // 输出: Lili
}

在这里,nickname并没有存储在 String 对象内部,而是存储在 nameMap。这样,我们就能给 String 模拟“存储”扩展属性的行为。


4. 结论

为什么扩展属性不能有 field

  1. 扩展并不会真正修改类,只是在外部“假装”给类添加了新功能
  2. 类的结构不会变,所以扩展属性不能有实际的存储空间(field)。
  3. 扩展属性只能通过 get()set() 访问数据,而不能存储数据
    如果你确实需要存储额外的值,可以用 外部存储(如 Map 来模拟存储属性的行为。

更新: 2025-03-24 11:55:15
原文: https://www.yuque.com/dongpozhouzi-mshe3/zhm85g/rp8ls7atil26dvvz


相关笔记