1. 概述

在实际开发中,我们有时会使用可变集合来维护内部状态,但在对外暴露时希望使用不可变集合,以防止接口被滥用。本文将介绍几种将可变集合转换为不可变集合的常见方式。

2. Kotlin 内置的不可变集合方法

Kotlin 提供了内置的集合类型,如 MutableListList,我们可以通过这些类型实现集合的转换。

先来看一个例子,创建一个可变列表并添加元素:

val mutableList = mutableListOf<String>()
mutableList.add("Hello")
println(mutableList.joinToString()) // 输出 "Hello"

接着,将其转换为不可变列表:

val immutableList: List<String> = mutableList.toList()

此时尝试修改该不可变列表:

immutableList[0] = "World" // 编译错误

会报错,说明不可变性得到了保证。

我们也可以将不可变列表再转为可变版本进行修改:

val backToMutableList = immutableList.toMutableList()
backToMutableList[0] = "World"
println(backToMutableList.joinToString()) // 输出 "World"
println(mutableList.joinToString())       // 输出 "Hello"

✅ 优点:使用简单,适合大多数日常场景
❌ 缺点:如果集合非常大,toList() 会进行一次完整的复制,可能影响性能

2.1. 仅通过类型转换的“伪不可变”

如果我们只是简单地将 MutableList 赋值给 List 类型:

val immutableList: List<String> = mutableList

虽然不能直接修改:

immutableList[0] = "World" // 编译错误

用户仍然可以通过类型强转恢复可变性

val backToMutableList = immutableList as MutableList<String>
backToMutableList[0] = "World"
println(backToMutableList.joinToString()) // 输出 "World"
println(mutableList.joinToString())       // 输出 "World"

⚠️ 这种方式无法真正保护数据安全,属于“伪不可变”。

3. 当内置方法不够用:使用委托封装

如果不想进行完整的复制,也不想让用户轻易绕过限制,可以考虑使用 Kotlin 的委托机制来封装原始集合。

定义一个不可变列表的包装类:

class ImmutableList<T>(private val protectedList: List<T>) : List<T> by protectedList

使用方式如下:

val immutableList = ImmutableList(mutableList)

尝试修改或强转会失败:

immutableList[0] = "World" // 编译错误
val backToMutable = immutableList as MutableList<String> // 编译错误

✅ 优点:无需复制数据,又能防止类型强转
❌ 缺点:需要手动实现包装类

3.1. 使用 Klutter 库简化实现

开源库 Klutter 已经为我们实现了基于委托的不可变集合封装。

添加依赖:

<dependency>
    <groupId>uy.kohesive.klutter</groupId>
    <artifactId>klutter-core</artifactId>
    <version>3.0.0</version>
</dependency>

示例使用方式:

val map = mutableMapOf<String, String>()
val set = mutableSetOf<String>()

val immutableMap = map.toImmutable()     // 深拷贝 + 不可变封装
val readOnlySet = set.asReadOnly()       // 仅封装,不复制

Klutter 提供了多种封装策略,具体实现可以参考其源码:Immutable.kt

4. 总结

方法 是否复制 是否防止强转 适用场景
toList() ✅ 是 ✅ 是 一般场景,性能不敏感
类型转换 ❌ 否 ❌ 否 临时封装,不推荐用于保护数据
委托封装 ❌ 否 ✅ 是 需要高性能且防止滥用
Klutter 库 可选 ✅ 是 推荐使用,封装完善

如需完整示例代码,可访问:GitHub 项目地址


原始标题:Convert a Mutable Collection Into an Immutable One in Kotlin