kotlin-tooling-immutable-collections-0-5-x-migration

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

kotlinx.collections.immutable 0.5.x Migration

kotlinx.collections.immutable 0.5.x 迁移指南

The 0.5.x line renames every copy-returning method on the persistent collections to a participial form (per [KEEP-0459]) and deprecates the old names at
WARNING
level with a
ReplaceWith
hint. Migrating is a mechanical, binary-compatible, semantics-preserving call-site rename — same parameters, order, and return type; only the name changes.
Drive it from the compiler: bump the version, recompile, and fix each deprecation warning — the warning names the replacement. Source of truth: [
0.5.0-MIGRATION.md
].
0.5.x 系列根据 [KEEP-0459] 提案,将持久化集合上所有返回副本的方法重命名为分词形式,并将旧方法标记为 WARNING 级别的废弃,同时附带 ReplaceWith 提示。迁移过程是机械性的、二进制兼容且语义一致的调用点重命名——参数、顺序和返回类型均保持不变,仅方法名称变更。
迁移由编译器驱动:升级版本、重新编译,然后修复每个废弃警告——警告信息会指明替代方法。权威参考:[
0.5.0-MIGRATION.md
]。

When it applies

适用场景

Check the version the project currently uses:
  • 0.3.x or 0.4.x (any pre-0.5.0) → run the migration below.
  • On 0.5.x but not the latest → set the version to the latest 0.5.x and stop. All 0.5.x releases share the same renames, so a within-line bump adds no new deprecations and needs no recompile.
  • On the latest 0.5.x, or on 0.6.x and later → nothing to do.
检查项目当前使用的版本:
  • 0.3.x 或 0.4.x(任何 0.5.0 之前的版本)→ 执行以下迁移步骤。
  • 已使用 0.5.x 但非最新版本→ 将版本设置为最新的 0.5.x 即可停止操作。所有 0.5.x 版本共享相同的重命名规则,因此同系列内升级不会新增废弃警告,无需重新编译。
  • 已使用最新 0.5.x 或 0.6.x 及更高版本→ 无需任何操作。

Migration

迁移步骤

1. Find the build command

1. 确定构建命令

Check
README.md
,
CLAUDE.md
, or
AGENTS.md
for how the project builds; if it isn't written down, infer it from the build files — Gradle (
./gradlew
), Maven (
mvn
, or the
./mvnw
wrapper), Bazel (a
bazel
wrapper), or a custom script. Record the compile command (and the test command). In a multi-module project you only need the modules that use the library, plus any you change — not a whole-repo build.
查看
README.md
CLAUDE.md
AGENTS.md
了解项目构建方式;若未记录,可从构建文件推断——Gradle(
./gradlew
)、Maven(
mvn
./mvnw
包装器)、Bazel(
bazel
包装器)或自定义脚本。记录编译命令(以及测试命令)。在多模块项目中,仅需处理使用该库的模块及变更模块——无需全仓库构建。

2. Baseline compile

2. 基准编译

Compile on the current version and confirm it's green. If it doesn't build now, you can't tell post-migration errors from pre-existing ones — get a working compile command first.
使用当前版本编译并确认构建成功。若当前无法构建,则无法区分迁移后错误与原有错误——需先确保编译命令可正常运行。

3. Bump to the latest 0.5.x

3. 升级至最新 0.5.x 版本

Find where the version is pinned —
grep -rn kotlinx-collections-immutable
across the build files finds it (version catalog, build script,
gradle.properties
,
pom.xml
, …) — and set it to the latest 0.5.x on [Maven Central] (a
-beta
is fine). If the build pins artifact hashes (e.g.
gradle/verification-metadata.xml
), update those too — the cheapest fix is to copy the new artifact's checksum straight from the dependency-verification failure message and add just that one entry, rather than regenerating the whole metadata file. The bump is binary-compatible; old code keeps compiling with warnings. (If the dependency fails to resolve with a Kotlin metadata-version error, the project's Kotlin is too old for the 0.5.x artifact — bump Kotlin first.)
查找版本固定位置——在构建文件中执行
grep -rn kotlinx-collections-immutable
可找到(版本目录、构建脚本、
gradle.properties
pom.xml
等),并将其设置为 [Maven Central] 上最新的 0.5.x 版本(
-beta
版本也可)。若构建固定了 artifact 哈希值(如
gradle/verification-metadata.xml
),也需更新这些哈希值——最简单的修复方式是直接从依赖验证失败消息中复制新 artifact 的校验和并添加单个条目,而非重新生成整个元数据文件。版本升级是二进制兼容的;旧代码仍可编译,但会出现警告。(若依赖解析时出现 Kotlin 元数据版本错误,说明项目的 Kotlin 版本过旧,无法适配 0.5.x 版本——需先升级 Kotlin。)

4. Recompile and fix the warnings

4. 重新编译并修复警告

Recompile. Each renamed method carries
@Deprecated(WARNING, ReplaceWith(...))
, so the compiler emits one warning per call site naming the replacement (e.g. "Use removingAll() instead"). Apply that rename. Repeat compile → fix until no
kotlinx.collections.immutable
deprecation warnings remain. (For multiplatform, one target compile surfaces the shared call sites. Pre-existing factory deprecations such as
immutableListOf
persistentListOf
appear the same way — apply those too.) A recompile that fails right after the bump is failing on these deprecations (plus, if hashes are pinned, a one-time dependency-verification error) — keep applying the renames the warnings name; don't re-run dependency-resolution or metadata-regeneration commands to try to clear it.
Trust the compiler — never find/replace by name. The same method names exist on
MutableList
/
MutableMap
/
MutableSet
and on the
.Builder
types, which mutate in place and are not deprecated. Only the sites the compiler flags (receiver statically
Persistent*
) get renamed; if it didn't flag it, leave it.
An
Unresolved reference
after a rename means the participial name isn't on that receiver — you've split a rename.
A rename only compiles if the declaration and every call site move together. The library already did that for the kotlinx types, so renaming their call sites just works — but it doesn't hold for anything else that merely shares the names. When a renamed call won't resolve, there are two cases:
  • The receiver is unrelated to this library (a
    Mutable*
    , a
    .Builder
    , a same-named method on some other type) — the rename was wrong; revert that site.
  • The receiver is a project type the codebase is itself migrating — it implements a
    Persistent*
    , or it's the project's own wrapper whose methods echo these names and get renamed to match. The rename is right but half-done: rename the declaration and its other callers too, so the call resolves. (Deprecated overrides on an implementer are step 5.)
Decide by the receiver's declared type, never the method name — a
Persistent*
-named field may hold another type. This matters most when you can't lean on a fast recompile and are renaming from reading the source.
Java callers. The recompile flags them only if the build reports javac deprecation warnings (
-Xlint:deprecation
, usually off). If it doesn't, grep the
.java
files that import the library for the old names and rename the calls whose receiver is a
Persistent*
type.
After the renames, the compiler may report some
@Suppress("DEPRECATION")
as having no effect — remove those (re-read the region first, in case it still covers something else).
重新编译。每个重命名的方法都带有
@Deprecated(WARNING, ReplaceWith(...))
注解,因此编译器会为每个调用点生成一条警告,指明替代方法(例如:"Use removingAll() instead")。根据提示进行重命名。重复编译→修复流程,直至不再出现
kotlinx.collections.immutable
的废弃警告。(对于多平台项目,单个目标编译即可显示共享调用点。原有的工厂方法废弃提示,如
immutableListOf
persistentListOf
,也会以相同方式显示——同样需进行重命名。)升级后立即编译失败通常是因为这些废弃警告(加上哈希值固定时的一次性依赖验证错误)——持续根据警告提示进行重命名即可;无需重新运行依赖解析或元数据生成命令来尝试清除错误。
信任编译器——切勿通过名称查找替换。
MutableList
/
MutableMap
/
MutableSet
以及
.Builder
类型上存在相同的方法名称,这些方法是原地修改的,不会被废弃。仅编译器标记的调用点(接收者静态类型为
Persistent*
)需要重命名;若未标记,则保留原样。
重命名后出现
Unresolved reference
错误,说明分词形式的方法不存在于该接收者上——你拆分了重命名操作。
只有当声明所有调用点同时迁移时,重命名才能编译通过。该库已针对 kotlinx 类型完成此操作,因此重命名其调用点即可正常工作——但对于其他仅共享名称的类型则不适用。当重命名后的调用无法解析时,有两种情况:
  • 接收者与该库无关(
    Mutable*
    .Builder
    、其他类型上的同名方法)——重命名错误;恢复该调用点。
  • 接收者是代码库自身正在迁移的项目类型——它实现了
    Persistent*
    ,或者是项目自己的包装类,其方法名称与这些方法一致,需要同步重命名。重命名是正确的但未完成:同时重命名声明及其其他调用者,使调用可解析。(实现类上的废弃重写见步骤5。)
根据接收者的声明类型判断,切勿仅根据方法名称——名为
Persistent*
的字段可能持有其他类型。当无法依赖快速重新编译而只能通过阅读源码进行重命名时,这一点尤为重要。
Java 调用者。 只有当构建报告 javac 废弃警告(
-Xlint:deprecation
,通常默认关闭)时,编译器才会标记这些调用点。若未开启,需在导入该库的
.java
文件中搜索旧方法名称,并对接收者为
Persistent*
类型的调用进行重命名。
重命名后,编译器可能会报告某些
@Suppress("DEPRECATION")
注解无效——移除这些注解(先重新阅读相关区域,确保其未覆盖其他内容)。

5. Custom implementers

5. 自定义实现类

If the project has classes that implement
PersistentList
/
PersistentMap
/
PersistentSet
/
PersistentCollection
, their deprecated overrides need migrating too. Find them:
bash
grep -rnE --include='*.kt' \
  '(class|object|interface)\s+\w[^:]*:\s*[^{]*\b(PersistentList|PersistentMap|PersistentSet|PersistentCollection)\s*<' .
On Windows PowerShell,
Select-String
is the
grep
equivalent:
powershell
Get-ChildItem -Recurse -Filter *.kt |
  Select-String '(class|object|interface)\s+\w[^:]*:\s*[^{]*\b(PersistentList|PersistentMap|PersistentSet|PersistentCollection)\s*<'
(Confirm a match really lists the interface as a supertype, not just a field type or type argument.) For each, move the implementation into the new participial method and have the deprecated override delegate to it:
kotlin
override fun adding(element: E): MyList<E> = /* real implementation */

@Suppress("OVERRIDE_DEPRECATION")
override fun add(element: E): MyList<E> = adding(element)
If the participial methods call each other, route those calls through participial siblings, not the deprecated names. (Add
"DEPRECATION"
to the suppress only when an override body itself still calls a deprecated member.) Doing this now matters: at 0.6.0 the old names become compile errors, and at 0.7.0 they are removed. See [
0.5.0-MIGRATION.md
] for the upstream implementer guidance.
若项目中有实现
PersistentList
/
PersistentMap
/
PersistentSet
/
PersistentCollection
的类,其废弃的重写方法也需要迁移。查找这些类:
bash
grep -rnE --include='*.kt' \\
  '(class|object|interface)\\s+\\w[^:]*:\\s*[^{]*\\b(PersistentList|PersistentMap|PersistentSet|PersistentCollection)\\s*<' .
在 Windows PowerShell 中,
Select-String
等同于
grep
powershell
Get-ChildItem -Recurse -Filter *.kt |
  Select-String '(class|object|interface)\\s+\\w[^:]*:\\s*[^{]*\\b(PersistentList|PersistentMap|PersistentSet|PersistentCollection)\\s*<'
(确认匹配结果确实将该接口列为父类型,而非仅字段类型或类型参数。)对于每个匹配类,将实现逻辑迁移至新的分词形式方法,并让废弃的重写方法委托给新方法:
kotlin
override fun adding(element: E): MyList<E> = /* 实际实现逻辑 */

@Suppress("OVERRIDE_DEPRECATION")
override fun add(element: E): MyList<E> = adding(element)
若分词形式方法之间相互调用,应通过分词形式的兄弟方法进行路由,而非调用废弃名称。(仅当重写方法体本身仍调用废弃成员时,才在抑制注解中添加
"DEPRECATION"
。)现在完成此步骤至关重要:在 0.6.0 版本中,旧方法名称将变为编译错误,而在 0.7.0 版本中会被移除。有关上游实现者指南,请参阅 [
0.5.0-MIGRATION.md
]。

6. Run any documented follow-up steps

6. 执行文档中记录的后续步骤

Do this after the renames compile clean, so that if you run low on time the call-site work is already done. Some projects document steps to run after a dependency change that the compiler won't surface — most commonly regenerating dependency-verification metadata (the
gradle/verification-metadata.xml
hashes from step 3). Usually the single-entry fix from step 3 is all you need; only fall back to the project's documented full-regeneration procedure (in
README.md
/
CONTRIBUTING.md
/
CLAUDE.md
/
AGENTS.md
) if that one entry isn't enough. Run it once — a full
--write-verification-metadata
/ "resolve all dependencies" pass re-resolves the entire graph and is slow, and repeating it rarely changes the outcome. Then re-confirm the build is clean.
在重命名后编译通过再执行此步骤,这样即使时间不足,调用点的修改工作也已完成。部分项目会记录依赖变更后的后续步骤,这些步骤不会被编译器提示——最常见的是重新生成依赖验证元数据(步骤3中的
gradle/verification-metadata.xml
哈希值)。通常步骤3中的单条目修复即可满足需求;仅当该条目不足时,才回退到项目文档中记录的完整生成流程(
README.md
/
CONTRIBUTING.md
/
CLAUDE.md
/
AGENTS.md
)。仅执行一次——完整的
--write-verification-metadata
/ "解析所有依赖" 过程会重新解析整个依赖图,速度较慢,重复执行几乎不会改变结果。然后再次确认构建成功。

Rename reference

重命名参考

  • PersistentCollection
    add
    adding
    ,
    addAll
    addingAll
    ,
    remove
    removing
    ,
    removeAll
    removingAll
    ,
    retainAll
    retainingAll
    ,
    clear
    cleared
  • PersistentList
    (the above, plus) —
    add(i, e)
    addingAt
    ,
    addAll(i, c)
    addingAllAt
    ,
    set(i, e)
    replacingAt
    ,
    removeAt
    removingAt
  • PersistentMap
    put
    putting
    ,
    putAll
    puttingAll
    ,
    remove(k)
    removing
    ,
    remove(k, v)
    removing
    ,
    clear
    cleared
Builders (
PersistentList.Builder
, etc.) are not renamed — they mutate in place, so their imperative names stay.
  • PersistentCollection
    add
    adding
    addAll
    addingAll
    remove
    removing
    removeAll
    removingAll
    retainAll
    retainingAll
    clear
    cleared
  • PersistentList
    (包含上述方法,新增)——
    add(i, e)
    addingAt
    addAll(i, c)
    addingAllAt
    set(i, e)
    replacingAt
    removeAt
    removingAt
  • PersistentMap
    put
    putting
    putAll
    puttingAll
    remove(k)
    removing
    remove(k, v)
    removing
    clear
    cleared
构建器(
PersistentList.Builder
等)不会被重命名——它们是原地修改的,因此保留命令式名称。

Links

链接

",