跳到主要内容

Kotlin 2.4 集合字面量实战:用方括号语法告别 listOf 和 mutableListOf

· 阅读需 5 分钟
重庆 Kotlin User Group

如果你写过 Kotlin,一定对下面这种代码再熟悉不过了:

val names = listOf("Alice", "Bob", "Charlie")
val scores = mutableListOf(95, 87, 72)
val matrix = listOf(listOf(1, 0), listOf(0, 1))

每次创建集合都要敲一遍 listOf() 或 mutableListOf(),嵌套的时候更是括号套娃。Kotlin 2.4 带来了集合字面量(Collection Literals)——一个期待已久的实验性特性,让你可以直接用方括号 [] 创建集合,就像写 JSON 数组一样自然。

基本语法:方括号就是一切​

集合字面量的核心思路非常简单:用 [] 代替 listOf()。

// 传统写法
val fruits: List<String> = listOf("apple", "banana", "cherry")

// 集合字面量写法
val fruits: List<String> = ["apple", "banana", "cherry"]

当你显式声明了类型,编译器会根据类型推断应该创建哪种集合:

val mutable: MutableList<String> = ["hello", "world"]
// 等价于 mutableListOf("hello", "world")

val immutable: List<String> = ["hello", "world"]
// 等价于 listOf("hello", "world")

没有显式类型声明时,编译器默认推断为 List:

val numbers = [1, 2, 3, 4, 5]
// 推断为 List<Int>

嵌套字面量:矩阵就这么简单​

集合字面量最让人兴奋的场景是创建嵌套结构。告别层层嵌套的 listOf(listOf(...)):

// 传统写法——括号地狱
val matrix = listOf(
listOf(1, 0, 0),
listOf(0, 1, 0),
listOf(0, 0, 1)
)

// 集合字面量——一目了然
val matrix: List<List<Int>> = [
[1, 0, 0],
[0, 1, 0],
[0, 0, 1]
]

这种语法在数据建模、配置定义、测试数据构造等场景中尤其好用。

自定义类型适配:operator fun of​

集合字面量并不仅限于标准库的 List 和 MutableList。通过定义 operator fun of,你可以让任何自定义类型都支持 [] 语法。这是这个特性最强大的地方。

来看一个实际例子——自定义矩阵类型:

class DoubleMatrix(vararg val rows: Row) {
companion object {
operator fun of(vararg rows: Row) = DoubleMatrix(*rows)
}

class Row(vararg val elements: Double) {
companion object {
operator fun of(vararg elements: Double) = Row(*elements)
}
}

override fun toString(): String =
rows.joinToString("\n") { row ->
row.elements.joinToString(", ")
}
}

fun main() {
// 用字面量语法创建单位矩阵
val identity: DoubleMatrix = [
[1.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, 0.0, 1.0]
]
println(identity)
}

编译器会将 [1.0, 0.0, 0.0] 解析为 Row.of(1.0, 0.0, 0.0),再将外层 [..., ..., ...] 解析为 DoubleMatrix.of(row1, row2, row3)。整个过程是递归解析的,类型推断会帮你找到正确的 of 函数。

再来看一个更贴近业务的例子——用字面量语法定义路由配置:

data class Route(val path: String, val method: String, val handler: String) {
companion object {
operator fun of(vararg routes: Route) = routes.toList()
}
}

val apiRoutes: List<Route> = [
Route("/users", "GET", "listUsers"),
Route("/users/{id}", "GET", "getUser"),
Route("/users", "POST", "createUser"),
]

如何启用​

集合字面量在 Kotlin 2.4 中是实验性特性,需要手动开启编译器开关。

Gradle(Kotlin DSL):

kotlin {
compilerOptions {
freeCompilerArgs.add("-Xcollection-literals")
}
}

Gradle(Groovy DSL):

kotlin {
compilerOptions {
freeCompilerArgs += '-Xcollection-literals'
}
}

Maven:

<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<configuration>
<args>
<arg>-Xcollection-literals</arg>
</args>
</configuration>
</plugin>

如果你在项目中使用了 @OptIn 注解,还需要加上对应的实验性标记:

@OptIn(ExperimentalCollectionLiterals::class)
fun buildConfig() {
val settings = ["debug" to true, "verbose" to false]
// ...
}

当前限制​

作为一个实验性特性,集合字面量目前有一些需要注意的限制:

1. 不能直接创建 Java 集合类型

// ❌ 不支持——ArrayList 是 Java 定义的
val list: ArrayList<String> = ["a", "b", "c"]

// ✅ 支持——Kotlin 标准库类型
val list: MutableList<String> = ["a", "b", "c"]

2. 类型推断依赖上下文

如果编译器无法从上下文推断出具体类型,会默认使用 List。在复杂场景中,建议显式声明类型以避免意外行为。

3. Set 和 Map 需要额外适配

目前 [] 语法默认映射到 List 系列。如果需要创建 Set,你需要显式声明类型或定义自己的 operator fun of。

实战场景:让测试数据更可读​

集合字面量最大的价值在于提升代码的可读性。在构造测试数据、配置文件或 DSL 时,方括号语法比工厂函数调用更接近"数据本身的样子":

// 测试数据构造——清晰直观
@OptIn(ExperimentalCollectionLiterals::class)
class UserServiceTest {
val testUsers: List<User> = [
User("Alice", 25, Role.ADMIN),
User("Bob", 30, Role.USER),
User("Charlie", 28, Role.USER),
]

val expectedPermissions: Map<String, List<String>> = mapOf(
"Alice" to ["read", "write", "delete"],
"Bob" to ["read"],
)

@Test
fun `should return correct permissions`() {
val service = UserService(testUsers)
assertEquals(["read", "write", "delete"], service.getPermissions("Alice"))
}
}

与其他语言对比​

集合字面量让 Kotlin 在语法简洁性上更进一步,向其他现代语言看齐:

语言集合字面量语法
Python[1, 2, 3]
JavaScript[1, 2, 3]
Rustvec![1, 2, 3]
Swift[1, 2, 3]
Kotlin (2.4)[1, 2, 3] ✅

终于,Kotlin 开发者也可以享受方括号的简洁了。

总结​

Kotlin 2.4 的集合字面量特性带来了三个核心价值:

  1. 更少的样板代码:[1, 2, 3] 比 listOf(1, 2, 3) 更简洁
  2. 更强的可读性:嵌套结构不再被括号淹没
  3. 更好的扩展性:通过 operator fun of,任何类型都能享受字面量语法

虽然目前还是实验性特性,但已经足以在非生产代码中尝试。如果你在写测试、做原型、或者构建 DSL,不妨打开 -Xcollection-literals 开关,体验一下方括号带来的清爽。