为什么Jetpack Compose源码中Modifier接口与伴生对象使用相同名称?
问题解答
1. 编译器完全不会混淆这两个同名的定义
Kotlin语法会严格区分类型位置和值位置,你写的fun BodyContent(modifier: Modifier = Modifier)中:
- 冒号
:后面的是类型声明位,编译器只会去匹配类、接口、类型别名这类类型定义,所以这里识别到的是Modifier接口 - 等号
=后面的是默认值赋值位,需要传入的是接口的实例,编译器会自动匹配对应作用域下的伴生对象实例,所以这里识别到的是Modifier接口的伴生对象
两者的使用场景完全没有重叠,编译期就会区分开,根本不会出现冲突。
2. 这个设计是Kotlin生态非常经典的合理API设计
这种设计本质是把「接口抽象」和「默认入口实例」的语义做了合并,兼顾了扩展性和易用性,核心好处有两个:
- 符合面向接口的设计原则:所有参数声明为
Modifier接口类型,可以兼容所有Modifier的实现类,不管是官方提供的padding、background等扩展,还是你自定义的Modifier,都可以直接传入,扩展性极强。 - 大幅降低API使用成本:伴生对象本身实现了
Modifier接口,是一个没有任何修饰效果的空实例,作为默认参数值刚好符合需求;同时你要构造新的Modifier链的时候,直接写Modifier.padding(8.dp).background(Color.White)就可以,不需要额外记一个空实例的名字比如EmptyModifier,调用起来非常简洁符合直觉。
源码注释里也明确写了,这个伴生对象就是专门设计来作为Modifier的默认值、以及修饰符链的起始入口用的,同名设计就是为了让使用者不需要特意区分接口和实例,只要记住Modifier就是修饰符的统一入口就行。
这个设计模式在Kotlin开发中非常常见,除了Compose的Modifier之外,很多Kotlin实现的库都会用类似的设计来简化API调用,属于行业通用的优秀实践。
附代码参考
代码A
@Composable fun BodyContent(modifier: Modifier = Modifier) { Row(modifier = modifier .background(color = Color.LightGray) .padding(16.dp) .size(200.dp) .horizontalScroll(rememberScrollState()), content = { StaggeredGrid { for (topic in topics) { Chip(modifier = Modifier.padding(8.dp), text = topic) } } }) }
代码B(Modifier核心源码)
interface Modifier { fun <R> foldIn(initial: R, operation: (R, Element) -> R): R fun <R> foldOut(initial: R, operation: (Element, R) -> R): R ... interface Element : Modifier { override fun <R> foldIn(initial: R, operation: (R, Element) -> R): R = operation(initial, this) override fun <R> foldOut(initial: R, operation: (Element, R) -> R): R = operation(this, initial) override fun any(predicate: (Element) -> Boolean): Boolean = predicate(this) override fun all(predicate: (Element) -> Boolean): Boolean = predicate(this) } /** * 伴生对象`Modifier`是为空的、默认的、初始的[Modifier],不包含任何[Element]元素。 * 你可以用它配合Modifier扩展函数创建新的[Modifier],或者作为Composable函数中Modifier参数的默认值。 */ companion object : Modifier { override fun <R> foldIn(initial: R, operation: (R, Element) -> R): R = initial override fun <R> foldOut(initial: R, operation: (Element, R) -> R): R = initial override fun any(predicate: (Element) -> Boolean): Boolean = false override fun all(predicate: (Element) -> Boolean): Boolean = true override infix fun then(other: Modifier): Modifier = other override fun toString() = "Modifier" } }
内容的提问来源于stack exchange,提问作者HelloCW
相关产品推荐
相关产品推荐

