兼容性指南:Kotlin 1.3
保持语言现代性 和 舒适的更新 是 Kotlin 语言设计中的基本原则。前者指出应移除阻碍语言演进的结构,后者则要求这种移除应提前充分沟通,以使代码迁移尽可能顺畅。
虽然大多数语言变更已通过更新日志或编译器警告等其他渠道公布,但本文档将它们全部汇总,为从 Kotlin 1.2 迁移到 Kotlin 1.3 提供完整参考。
基本术语
本文档中,我们引入了几种兼容性:
- 源代码: 源代码不兼容的变更会阻止过去正常编译(无错误或警告)的代码继续编译。
- 二进制: 如果互换两个二进制 artifact 不会导致加载或链接错误,则称它们二进制兼容。
- 行为: 如果同一程序在应用变更前后表现出不同的行为,则称该变更行为不兼容。
请记住,这些定义仅适用于纯 Kotlin。从其他语言(例如 Java)视角来看的 Kotlin 代码兼容性超出了本文档的范围。
不兼容的变更
构造函数实参关于 <clinit>
调用的求值顺序
问题: KT-19532
组件: Kotlin/JVM
不兼容的变更类型: 行为
简述: 1.3 中关于类初始化的求值顺序已变更
弃用周期:
- <1.3: 旧行为(详见问题)
= 1.3: 行为已变更,可以使用
-Xnormalize-constructor-calls=disable
临时恢复到 1.3 之前的行为。对该标志的支持将在下一个主要版本中移除。
注解构造函数形参上缺少 getter 目标注解
问题: KT-25287
组件: Kotlin/JVM
不兼容的变更类型: 行为
简述: 1.3 中注解构造函数形参上的 getter 目标注解将正确写入 classfile
弃用周期:
- <1.3: 注解构造函数形参上的 getter 目标注解未应用
=1.3: 注解构造函数形参上的 getter 目标注解已正确应用并写入生成的代码
类构造函数的 @get:
注解中缺少错误报告
问题: KT-19628
组件: 核心语言
不兼容的变更类型: 源代码
简述: 1.3 中 getter 目标注解中的错误将正确报告
弃用周期:
- <1.2: getter 目标注解中的编译错误未报告,导致不正确的代码正常编译。
- 1.2.x: 错误仅由工具报告,编译器仍会在没有任何警告的情况下编译此类代码
=1.3: 错误也由编译器报告,导致错误代码被拒绝
访问带有 @NotNull 注解的 Java 类型时的可空性断言
问题: KT-20830
组件: Kotlin/JVM
不兼容的变更类型: 行为
简述: 对带有非空注解的 Java 类型生成的可空性断言将更具侵略性,导致在此处传递
null
的代码更快失败。弃用周期:
- <1.3: 当涉及类型推断时,编译器可能会漏掉此类断言,从而允许在针对二进制 artifact 编译期间进行潜在的
null
传播(详见问题)。=1.3: 编译器生成了缺失的断言。这可能会导致(错误地)在此处传递
null
的代码更快失败。可以使用-XXLanguage:-StrictJavaNullabilityAssertions
临时恢复到 1.3 之前的行为。对该标志的支持将在下一个主要版本中移除。
枚举成员上的不健全智能类型转换
问题: KT-20772
组件: 核心语言
不兼容的变更类型: 源代码
简述: 枚举项成员上的智能类型转换将正确地仅应用于该枚举项
弃用周期:
- <1.3: 枚举项成员上的智能类型转换可能导致其他枚举项的相同成员上的不健全智能类型转换。
=1.3: 智能类型转换将正确地仅应用于枚举项的成员。
-XXLanguage:-SoundSmartcastForEnumEntries
将临时恢复旧行为。对该标志的支持将在下一个主要版本中移除。
val 幕后字段在 getter 中的重新赋值
问题: KT-16681
组件: 核心语言
不兼容的变更类型: 源代码
简述: 在 getter 中重新赋值
val
属性的幕后字段现在已被禁止弃用周期:
- <1.2: Kotlin 编译器允许在
val
的 getter 中修改幕后字段。这不仅违反了 Kotlin 语义,还会生成行为不当的 JVM 字节码,从而重新赋值final
字段。- 1.2.X: 对重新赋值
val
幕后字段的代码报告弃用警告=1.3: 弃用警告提升为错误
for 循环迭代前对数组的捕获
问题: KT-21354
组件: Kotlin/JVM
不兼容的变更类型: 源代码
简述: 如果 for 循环区间中的表达式是循环体中更新的局部变量,此变更会影响循环执行。这与迭代其他容器(例如区间、字符序列和集合)不一致。
弃用周期:
- <1.2: 所描述的代码模式可正常编译,但对局部变量的更新会影响循环执行
- 1.2.X: 如果 for 循环中的区间表达式是数组类型的局部变量并在循环体中赋值,则报告弃用警告
- 1.3: 在此类情况下变更行为,以与其他容器保持一致
枚举项中的嵌套分类器
问题: KT-16310
组件: 核心语言
不兼容的变更类型: 源代码
简述: 从 Kotlin 1.3 开始,禁止在枚举项中嵌套分类器(类、object、接口、注解类、枚举类)
弃用周期:
- <1.2: 枚举项中的嵌套分类器可正常编译,但可能在运行时失败并抛出异常
- 1.2.X: 对嵌套分类器报告弃用警告
=1.3: 弃用警告提升为错误
数据类覆盖 copy
问题: KT-19618
组件: 核心语言
不兼容的变更类型: 源代码
简述: 从 Kotlin 1.3 开始,禁止数据类覆盖
copy()
弃用周期:
- <1.2: 覆盖
copy()
的数据类可正常编译,但可能在运行时失败/表现出奇怪的行为- 1.2.X: 对覆盖
copy()
的数据类报告弃用警告=1.3: 弃用警告提升为错误
继承 Throwable 且捕获外部类泛型参数的内部类
问题: KT-17981
组件: 核心语言
不兼容的变更类型: 源代码
简述: 从 Kotlin 1.3 开始,不允许内部类继承
Throwable
弃用周期:
- <1.2: 继承
Throwable
的内部类可正常编译。如果此类内部类恰好捕获了泛型参数,则可能导致在运行时失败的奇怪代码模式。- 1.2.X: 对继承
Throwable
的内部类报告弃用警告=1.3: 弃用警告提升为错误
关于涉及伴生对象复杂类层次结构的可见性规则
组件: 核心语言
不兼容的变更类型: 源代码
简述: 从 Kotlin 1.3 开始,对于涉及伴生对象和嵌套分类器的复杂类层次结构,短名称的可见性规则更加严格。
弃用周期:
- <1.2: 旧的可见性规则(详见问题)
- 1.2.X: 对不再可访问的短名称报告弃用警告。工具建议通过添加全限定名进行自动迁移。
=1.3: 弃用警告提升为错误。问题代码应添加全限定符或显式导入
vararg 注解的非常量形参
问题: KT-23153
组件: 核心语言
不兼容的变更类型: 源代码
简述: 从 Kotlin 1.3 开始,禁止将非常量值设置为 vararg 注解形参
弃用周期:
- <1.2: 编译器允许为 vararg 注解形参传递非常量值,但实际上在字节码生成期间会丢弃该值,导致非显而易见的行为
- 1.2.X: 对此类代码模式报告弃用警告
=1.3: 弃用警告提升为错误
局部注解类
问题: KT-23277
组件: 核心语言
不兼容的变更类型: 源代码
简述: 从 Kotlin 1.3 开始,不支持局部注解类
弃用周期:
- <1.2: 编译器可正常编译局部注解类
- 1.2.X: 对局部注解类报告弃用警告
=1.3: 弃用警告提升为错误
局部委托属性上的智能类型转换
问题: KT-22517
组件: 核心语言
不兼容的变更类型: 源代码
简述: 从 Kotlin 1.3 开始,不允许对局部委托属性进行智能类型转换
弃用周期:
- <1.2: 编译器允许对局部委托属性进行智能类型转换,这在委托行为不当的情况下可能导致不健全智能类型转换
- 1.2.X: 局部委托属性上的智能类型转换被报告为已弃用(编译器发出警告)
=1.3: 弃用警告提升为错误
mod 操作符约定
问题: KT-24197
组件: 核心语言
不兼容的变更类型: 源代码
简述: 从 Kotlin 1.3 开始,禁止声明
mod
操作符,以及解析为此类声明的调用弃用周期:
- 1.1.X, 1.2.X: 对
operator mod
的声明以及解析到它的调用报告警告- 1.3.X: 将警告提升为错误,但仍允许解析到
operator mod
声明- 1.4.X: 不再将调用解析到
operator mod
以具名形式向 vararg 传递单个元素
问题: KT-20588, KT-20589。另请参见 KT-20171
组件: 核心语言
不兼容的变更类型: 源代码
简述: 在 Kotlin 1.3 中,向 vararg 赋值单个元素已被弃用,应替换为连续的展开和数组构造。
弃用周期:
- <1.2: 以具名形式向 vararg 赋值单个值元素可正常编译,并被视为向数组赋值单个元素,导致向 vararg 赋值数组时出现非显而易见的行为
- 1.2.X: 对此类赋值报告弃用警告,建议用户切换到连续的展开和数组构造。
- 1.3.X: 警告提升为错误
= 1.4: 变更向 vararg 赋值单个元素的语义,使数组赋值等同于数组展开的赋值
目标为 EXPRESSION 的注解的保留策略
问题: KT-13762
组件: 核心语言
不兼容的变更类型: 源代码
简述: 从 Kotlin 1.3 开始,目标为
EXPRESSION
的注解只允许SOURCE
保留策略弃用周期:
- <1.2: 目标为
EXPRESSION
且保留策略非SOURCE
的注解是允许的,但在使用点被静默忽略- 1.2.X: 对此类注解的声明报告弃用警告
=1.3: 警告提升为错误
目标为 PARAMETER 的注解不应适用于形参的类型
问题: KT-9580
组件: 核心语言
不兼容的变更类型: 源代码
简述: 从 Kotlin 1.3 开始,当目标为
PARAMETER
的注解应用于形参类型时,将正确报告关于错误注解目标的错误弃用周期:
- <1.2: 上述代码模式可正常编译;注解被静默忽略且不存在于字节码中
- 1.2.X: 对此类用法报告弃用警告
=1.3: 警告提升为错误
当索引超出范围时 Array.copyOfRange 抛出异常而不是扩大返回的数组
问题: KT-19489
组件: kotlin-stdlib (JVM)
不兼容的变更类型: 行为
简述: 从 Kotlin 1.3 开始,确保
Array.copyOfRange
的toIndex
实参(表示要复制的区间的排他性末尾)不大于数组大小,如果大于则抛出IllegalArgumentException
。弃用周期:
- <1.3: 如果
Array.copyOfRange
调用中的toIndex
大于数组大小,区间中缺失的元素将填充null
,违反 Kotlin 类型系统的健全性。=1.3: 检测
toIndex
是否在数组边界内,如果不在则抛出异常
步长为 Int.MIN_VALUE 和 Long.MIN_VALUE 的 Int 和 Long 数列已被禁用,不允许实例化
问题: KT-17176
组件: kotlin-stdlib (JVM)
不兼容的变更类型: 行为
简述: 从 Kotlin 1.3 开始,禁止整数数列的步长值为其整数类型 (
Long
或Int
) 的最小负值,因此调用IntProgression.fromClosedRange(0, 1, step = Int.MIN_VALUE)
将抛出IllegalArgumentException
弃用周期:
- <1.3: 过去可以创建步长为
Int.MIN_VALUE
的IntProgression
,它会产生两个值[0, -2147483648]
,这种行为非显而易见=1.3: 如果步长是其整数类型的最小负值,则抛出
IllegalArgumentException
非常长序列操作中的索引溢出检测
问题: KT-16097
组件: kotlin-stdlib (JVM)
不兼容的变更类型: 行为
简述: 从 Kotlin 1.3 开始,确保
index
、count
和类似方法不会在长序列上溢出。受影响方法的完整列表请参见问题。弃用周期:
- <1.3: 在非常长的序列上调用此类方法可能由于整数溢出而产生负结果
=1.3: 在此类方法中检测溢出并立即抛出异常
统一各平台空匹配正则表达式的 split 结果
问题: KT-21049
组件: kotlin-stdlib (JVM)
不兼容的变更类型: 行为
简述: 从 Kotlin 1.3 开始,统一
split
方法在所有平台上通过空匹配正则表达式的行为弃用周期:
- <1.3: 所描述调用的行为在 JS、JRE 6、JRE 7 与 JRE 8+ 之间存在差异
=1.3: 统一各平台上的行为
编译器分发版中已停用的弃用构件
问题: KT-23799
组件: 其他
不兼容的变更类型: 二进制
简述: Kotlin 1.3 停用以下弃用的二进制构件:
kotlin-runtime
: 改用kotlin-stdlib
kotlin-stdlib-jre7/8
: 改用kotlin-stdlib-jdk7/8
- 编译器分发版中的
kotlin-jslib
: 改用kotlin-stdlib-js
弃用周期:
- 1.2.X: 这些构件被标记为已弃用,编译器在使用这些构件时报告警告
=1.3: 这些构件已停用
stdlib 中的注解
问题: KT-21784
组件: kotlin-stdlib (JVM)
不兼容的变更类型: 二进制
简述: Kotlin 1.3 从 stdlib 中移除了
org.jetbrains.annotations
包中的注解,并将它们移动到编译器附带的单独构件中:annotations-13.0.jar
和mutability-annotations-compat.jar
弃用周期:
- <1.3: 注解与 stdlib 构件一起发布
=1.3: 注解在单独的构件中发布