扩展函数的核心原理
-
语法层面的 "假象"
从语法上看,扩展函数似乎是直接 "注入" 到了目标类中,调用时也像调用类的成员函数一样(例如
对象.扩展函数()
)。但实际上,扩展函数并不会修改目标类的结构,也不会被编译成目标类的成员方法。 -
静态解析的工具函数
编译器会将扩展函数处理为静态工具函数。例如,在 Kotlin 中定义:
kotlinfun String.lastChar(): Char { return this[this.length - 1] }
编译器会将其转换为类似 Java 的静态方法:
javapublic static Char lastChar(String $this) { return $this.charAt($this.length() - 1); }
其中,
this
关键字在扩展函数中指向调用该函数的对象实例(即目标类的对象),但本质上是函数的第一个参数。 -
编译期绑定,无运行时开销
扩展函数的调用是编译期确定的,而非运行时动态绑定。编译器会根据调用者的静态类型(声明类型)来匹配对应的扩展函数,而不是根据运行时的实际类型。这意味着:
- 扩展函数不能覆盖类的成员函数(如果同名,优先调用成员函数)。
- 无法通过多态实现扩展函数的动态调用。
扩展函数的优势
- 避免类膨胀 :无需为扩展功能创建子类或工具类(如
StringUtils
),保持代码整洁。 - 增强可读性 :调用方式与成员函数一致(
对象.函数()
),符合直觉。 - 兼容第三方类:可以为无法修改源代码的类(如 Java 标准库类、第三方库类)添加功能。
局限性
- 无法访问私有 / 受保护成员:扩展函数只能访问目标类的公开成员,因为它本质上是外部函数。
- 静态绑定的限制:若子类和父类有同名扩展函数,调用结果由变量的声明类型决定,而非实际类型。
- 可能引发命名冲突:多个扩展函数同名时,需要通过显式导入或命名空间区分。
典型应用场景
- 为基础类型(如字符串、集合)添加便捷操作(如字符串判空、集合过滤)。
- 为第三方库类补充业务相关功能(如为
LocalDateTime
添加格式化方法)。 - 在不破坏原有设计的前提下,逐步扩展类的功能。
Compose实现点击防抖
要求:不破坏Modifier的设计原则的情况下,实现防抖功能
kotlin
fun Modifier.debounceClick(
debounceTime: Long = 2000,
onClick: () -> Unit
) = this.then( // 使用then()方法连接新的修饰符
Modifier.composed {
var lastClickTime by remember { mutableStateOf(0L) }
Modifier.clickable {
val currentTime = System.currentTimeMillis()
if (currentTime - lastClickTime > debounceTime) {
lastClickTime = currentTime
onClick()
}
}
}
)
then 是Modifier的方法,作用是用来组合Modifier,我们来看他的源码
kotlin
infix fun then(other: Modifier): Modifier =
//合并
if (other === Modifier) this else CombinedModifier(this, other)
composed 代码块 返回值是Modifier,composed也是将返回值进行合并,composed的源码
kotlin
fun Modifier.composed(
inspectorInfo: InspectorInfo.() -> Unit = NoInspectorInfo,
factory: @Composable Modifier.() -> Modifier
): Modifier = this.then(ComposedModifier(inspectorInfo, factory))
Modifier.clickable 是 factory的返回值 factory的返回值和ComposedModifier进行合并, 进行合并的结果,再和debounceClick的对象进行合并,这样就对Modifier进行了扩展。实现了点击防抖。 发现发明Modifier的人真是个天才。。。