扩展函数
扩展函数与扩展属性
概述
扩展函数(Extension Function)是 Kotlin 最具魅力的特性之一。它允许你为已有的类添加新方法,而不需要继承该类或使用装饰器模式。从使用者的角度看,扩展函数就像是被扩展类本身自带的方法一样。
扩展函数的本质是静态方法,它不会修改原始类,也不会影响原始类的行为。编译器只是把扩展函数调用转换为静态方法调用。理解这一点对于正确使用扩展函数至关重要。
基础概念
- 扩展函数:为已有类添加新方法,语法
fun ClassName.newMethod() { ... } - 扩展属性:为已有类添加新属性,语法
val ClassName.newProp: Type get() = ... - 接收者类型:被扩展的类,如
String是fun String.xxx()的接收者类型 - 接收者对象:调用扩展函数时,
this指向的对象 - 静态解析:扩展函数在编译时确定调用哪个函数,不涉及多态
快速上手
// 为 String 添加扩展函数
fun String.addExclamation(): String = this + "!"
// 为 Int 添加扩展函数
fun Int.isEven(): Boolean = this % 2 == 0
fun main() {
// 像调用普通方法一样调用扩展函数
println("Hello".addExclamation()) // Hello!
println(4.isEven()) // true
println(3.isEven()) // false
}
详细用法
扩展函数基础
// 为 String 添加扩展函数
fun String.wordCount(): Int = this.split(Regex("\\s+")).size
// 为 List 添加扩展函数
fun <T> List<T>.second(): T {
if (this.size < 2) throw NoSuchElementException("列表没有第二个元素")
return this[1]
}
// 为可空类型添加扩展函数
fun String?.isNullOrBlank(): Boolean = this == null || this.isBlank()
fun main() {
println("Hello World Kotlin".wordCount()) // 3
println(listOf(10, 20, 30).second()) // 20
println(null.isNullOrBlank()) // true
}
扩展属性
// 为 String 添加扩展属性
val String.firstChar: Char
get() = this.first()
val String.hasContent: Boolean
get() = this.isNotBlank()
// 为 Int 添加扩展属性
val Int.isPositive: Boolean
get() = this > 0
// 注意:扩展属性不能有 backing field,不能初始化
// 错误:val String.lastAccess: Long = System.currentTimeMillis()
// 因为扩展属性不会在对象中存储数据
fun main() {
println("Hello".firstChar) // H
println("Hello".hasContent) // true
println(42.isPositive) // true
}
扩展函数与可空类型
// 为非空类型添加扩展
fun String.trimToLength(maxLength: Int): String {
return if (this.length <= maxLength) this
else this.substring(0, maxLength) + "..."
}
// 为可空类型添加扩展
fun String?.safeLength(): Int = this?.length ?: 0
// 更安全的调用方式
fun String?.orElse(default: String): String = this ?: default
fun main() {
println("Hello World".trimToLength(5)) // Hello...
println(null.safeLength()) // 0
println(null.orElse("默认值")) // 默认值
}
扩展函数中的 this
// 在扩展函数中,this 指向接收者对象
fun String.describe(): String {
// this 指向调用该方法的 String 对象
return "字符串 '$this' 的长度是 ${this.length}"
}
// 可以省略 this
fun String.shout(): String = uppercase() + "!!!"
fun main() {
println("Hello".describe()) // 字符串 'Hello' 的长度是 5
println("hello".shout()) // HELLO!!!
}
泛型扩展函数
// 为任意类型添加扩展
fun <T> T.printSelf(): T {
println(this)
return this
}
// 带约束的泛型扩展
fun <T : Comparable<T>> T.isBetween(min: T, max: T): Boolean {
return this >= min && this <= max
}
// 为集合添加泛型扩展
fun <T> List<T>.interleave(other: List<T>): List<T> {
val result = mutableListOf<T>()
val maxSize = maxOf(this.size, other.size)
for (i in 0 until maxSize) {
if (i < this.size) result.add(this[i])
if (i < other.size) result.add(other[i])
}
return result
}
fun main() {
42.printSelf() // 42
println(5.isBetween(1, 10)) // true
println(listOf(1, 2, 3).interleave(listOf("a", "b", "c", "d")))
// [1, a, 2, b, 3, c, d]
}
扩展函数的解析规则
扩展函数是静态解析的,不是多态的:
open class Animal
class Dog : Animal()
// 为 Animal 添加扩展
fun Animal.sound() = "动物叫声"
// 为 Dog 添加扩展
fun Dog.sound() = "汪汪汪"
fun main() {
val dog: Dog = Dog()
println(dog.sound()) // 汪汪汪(调用 Dog 的扩展)
// 关键区别:声明类型决定调用哪个扩展
val animal: Animal = Dog()
println(animal.sound()) // 动物叫声(调用 Animal 的扩展,不是 Dog 的)
// 因为扩展函数是静态解析的,根据变量的声明类型决定
}
扩展函数的作用域
// 顶层扩展函数:在任何地方都可以使用(需要 import)
// File: StringUtils.kt
package com.example.utils
fun String.isEmail(): Boolean = this.contains("@") && this.contains(".")
// 使用时需要 import
// import com.example.utils.isEmail
// 成员扩展函数:只在类内部有效
class Parser {
// 这个扩展函数只在 Parser 类内部可用
private fun String.parseToInt(): Int? = this.toIntOrNull()
fun parse(input: String): Int? {
return input.parseToInt() // 在类内部可以使用
}
}
常见场景
工具函数
// 日期格式化扩展
fun java.time.LocalDateTime.formatChinese(): String {
return "${this.year}年${this.monthValue}月${this.dayOfMonth}日"
}
// 集合扩展
fun <T> List<T>.randomItem(): T = this.random()
fun <T> List<T>.randomItemOrNull(): T? = if (this.isEmpty()) null else this.random()
// 数值扩展
fun Double.roundTo(decimals: Int): Double {
var multiplier = 1.0
repeat(decimals) { multiplier *= 10 }
return kotlin.math.round(this * multiplier) / multiplier
}
fun main() {
val now = java.time.LocalDateTime.now()
println(now.formatChinese()) // 2026年6月14日
println(3.14159.roundTo(2)) // 3.14
}
防御性编程
// 安全的类型转换
inline fun <reified T> Any.safeCast(): T? = this as? T
// 安全的列表访问
fun <T> List<T>.safeGet(index: Int): T? {
return if (index in indices) this[index] else null
}
// 非空检查
fun <T : Any> T?.requireNonNull(lazyMessage: () -> String = { "值不能为空" }): T {
return this ?: throw IllegalArgumentException(lazyMessage())
}
fun main() {
val value: Any = "Hello"
val str: String? = value.safeCast()
println(str) // Hello
val list = listOf(1, 2, 3)
println(list.safeGet(5)) // null(不会越界异常)
}
流式 API
// 链式调用扩展
fun <T> T.applyIf(condition: Boolean, block: T.() -> Unit): T {
if (condition) block()
return this
}
fun <T> T.alsoIf(condition: Boolean, block: (T) -> Unit): T {
if (condition) block(this)
return this
}
fun main() {
val result = StringBuilder()
.applyIf(true) { append("Hello") }
.applyIf(false) { append(" World") }
.applyIf(true) { append(" Kotlin") }
.toString()
println(result) // Hello Kotlin
}
注意事项
- 扩展函数不是真正的方法:它不会修改原始类,只是编译器的语法糖
- 静态解析:扩展函数根据变量的声明类型决定调用哪个,不是运行时类型
- 不要滥用扩展:如果一个函数与类没有紧密关系,用顶层函数更合适
- 命名冲突:如果扩展函数与成员方法同名,成员方法优先
- 扩展属性不能有状态:没有 backing field,不能存储数据
- 避免在扩展函数中做耗时操作:扩展函数应该轻量,复杂逻辑放在普通函数中
进阶用法
中缀扩展函数
// 中缀函数让调用更自然
infix fun String.times(n: Int): String {
return this.repeat(n)
}
infix fun <T> List<T>.intersect(other: List<T>): List<T> {
return this.filter { it in other }
}
fun main() {
println("Ha" times 3) // HaHaHa
println("Ha" times 3) // 等价于 "Ha".times(3)
println(listOf(1,2,3) intersect listOf(2,3,4)) // [2, 3]
}
扩展函数与运算符重载
// 为 Range 添加扩展
operator fun ClosedRange<Int>.step(step: Int): Iterable<Int> {
return object : Iterable<Int> {
override fun iterator(): Iterator<Int> {
var current = start
return object : Iterator<Int> {
override fun hasNext() = if (step > 0) current <= endInclusive else current >= endInclusive
override fun next(): Int {
val result = current
current += step
return result
}
}
}
}
}
fun main() {
for (i in (1..10) step 2) {
print("$i ") // 1 3 5 7 9
}
}
带接收者的函数类型
// 函数类型的接收者
fun buildString(builderAction: StringBuilder.() -> Unit): String {
val builder = StringBuilder()
builder.builderAction() // 在 StringBuilder 的上下文中执行
return builder.toString()
}
fun main() {
val result = buildString {
// 这里的 this 是 StringBuilder
append("Hello")
append(" ")
append("World")
}
println(result) // Hello World
}