Kotlin 提供了一些便捷函数,可用于创建集合、字符串等,无需编写通常的样板代码。
在这篇简短的文章中,我们将探索 Kotlin 标准库中的几个常用 Builder 函数,这些函数让创建这些对象变得更加容易。
创建List
创建并填充列表的常见方法,例如有:
val newList = mutableListOf<Int>()
newList.add(1)
if (conditionFullfilled) {
newList.add(2)
}
或者
val newList = mutableListOf<Int>().apply {
add(1)
if (conditionFullfilled) {
add(2)
}
}
我们可以使用 buildList {}
函数使操作更简便。该函数会在内部创建一个可变列表,允许我们对其调用函数,然后返回一个不可变列表。
val newList = buildList {
add(1)
if (conditionFullfilled) {
add(2)
}
}
如果查看 buildList()
函数的实现,我们可以看到它的工作方式与我们最初的代码类似。
它调用 buildListInternal()
函数来构建一个新的可变列表,并对其应用相关操作。
由于它是一个内联函数,这也意味着额外 lambda 表达式的开销被消除了,因为它会将代码复制到调用点。
@SinceKotlin("1.6")
@WasExperimental(ExperimentalStdlibApi::class)
@kotlin.internal.InlineOnly
@Suppress("LEAKED_IN_PLACE_LAMBDA", "WRONG_INVOCATION_KIND")
public inline fun <E> buildList(@BuilderInference builderAction: MutableList<E>.() -> Unit): List<E> {
contract { callsInPlace(builderAction, InvocationKind.EXACTLY_ONCE) }
return buildListInternal(builderAction)
}
创建String
通常根据某些条件拼接字符串的方法是创建一个 StringBuilder
对象,然后对其调用 append
函数,最后将其转换为字符串:
val sb = StringBuilder()
sb.append("Some")
if (conditionFullfilled) {
sb.append(" string")
}
val newString = sb.toString()
或者
val newString = StringBuilder().apply {
append("Some")
if (conditionFullfilled) {
append(" string")
}
}.toString()
我们可以使用 buildString {}
函数让操作更简便,该函数会为我们创建 StringBuilder
对象并返回最终的输出字符串。我们可以在代码块内部对其调用常用的 append
函数。
val newString = buildString {
append("Some")
if (conditionFullfilled) {
append(" string")
}
}
查看 buildString()
函数的实现,我们可以看到它的工作方式与我们最初的代码相同。
同样由于它是一个内联函数,这也意味着额外 lambda 表达式的开销被消除了。
@kotlin.internal.InlineOnly
public inline fun buildString(builderAction: StringBuilder.() -> Unit): String {
contract { callsInPlace(builderAction, InvocationKind.EXACTLY_ONCE) }
return StringBuilder().apply(builderAction).toString()
}
创建Set
与创建 List
和 String
类似,有一个辅助函数可用于创建 Set
。它会构建一个可变 Set
,允许我们对其调用相关函数,然后返回一个不可变 Set
。
val newSet = buildSet {
add(1)
if (conditionFullfilled) {
addAll(xxx)
}
}
buildSet()
函数可用于任何类型。
如果深入查看 buildSet()
的实现,我们会发现内部其实是使用的 Map
去实现的,也就是和下面创建 Map
是类似的逻辑。
当然了,Set
内部的实现本身也是 Map
。
创建Map
我们可以使用 buildMap {}
函数构建一个新的映射。它会在内部构建一个新的 MutableMap
,允许我们对其调用函数,然后返回一个不可变的 Map
。
val newMap = buildMap {
put("key", "value")
if (conditionFullfilled) {
putAll(someOtherMap)
}
}
上述函数都是 Kotlin 标准库的一部分,在大多数项目中应该都可以使用。
而 Android 中,有一些特殊的 Builder
,下面,我们将一一介绍他们。
创建AnnotatedString
Compose 中支持一种 AnnotatedString
,类似 SpannedString
,它主要作用是丰富文字显示的样式。
buildAnnotatedString {
// 红色的 "Hello"
withStyle(SpanStyle(color = Color.Red)) {
append("Hello")
}
append(" ")
// 蓝色的 "World!"
withStyle(SpanStyle(color = Color.Blue)) {
append("World!")
}
// 蓝色的链接
withLink(
LinkAnnotation.Url(
"https://developer.android.com/jetpack/compose",
TextLinkStyles(style = SpanStyle(color = Color.Blue))
)
) {
append("Jetpack Compose")
}
}
创建SpannedString
与 buildAnnotatedString
对应的则是 buildSpannedString
。
buildSpannedString {
backgroundColor(Color.RED) {
append("遮天是")
bold {
append("一群人")
}
append("的完美,完美是一个人的遮天")
}
}
它的用法和 buildAnnotatedString
是类似的。
如果你的项目添加了 Kotlinx Serialization 的支持,那么,你还能够得到构建 Json 的支持。
创建Json
buildJsonObject
能够帮助我们构建 JsonObject
:
buildJsonObject {
put("booleanKey", true)
putJsonArray("arrayKey") {
for (i in 1..10) add(i)
}
putJsonObject("objectKey") {
put("stringKey", "stringValue")
}
}
同理,还有 buildJsonArray
:
buildJsonArray {
add(true)
addJsonArray {
for (i in 1..10) add(i)
}
addJsonObject {
put("stringKey", "stringValue")
}
}
总结
Kotlin 标准库提供了几个便捷函数,用于创建集合和字符串等常见类型。
用一种标准化的方式避免常见的样板代码,是 Kotlin 的核心设计——让代码简洁并自说明。
同时对于 Android 和其他的第三方库,Kotlin 也提供了 Builder 的相关支持。
如果你在开发过程中不想使用 new
语法(这里只是沿用了 Java 的说法)创建样板代码,或者不知道如何传递那些烦人的构造函数参数,可以尝试使用 Builder 函数让代码看上去更直观。