Kotlin为了不让你们直接new操了多少心?

0 阅读4分钟

1.jpg

Kotlin 提供了一些便捷函数,可用于创建集合、字符串等,无需编写通常的样板代码。

在这篇简短的文章中,我们将探索 Kotlin 标准库中的几个常用 Builder 函数,这些函数让创建这些对象变得更加容易。

创建List

2.jpg

创建并填充列表的常见方法,例如有:

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

与创建 ListString 类似,有一个辅助函数可用于创建 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

3.jpg

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

4.png

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 函数让代码看上去更直观。