13.6.11.2 Kotlin 1.3.x 兼容性指南
10 分钟阅读
原文链接: https://kotlinlang.org/docs/compatibility-guide-13.html
13.6.11.2 Kotlin 1.3.x 兼容性指南
保持语言现代性 和 平滑升级 是 Kotlin 语言设计的基本原则。前者指出,阻碍语言演进的构造应当被移除;后者指出,这种移除应当事先充分沟通,以便代码迁移尽可能顺畅。
虽然大多数语言变更已经通过其他渠道公布(例如更新日志或编译器警告),本文仍然把它们全部汇总,为从 Kotlin 1.2 迁移到 Kotlin 1.3 提供完整参考。
基本术语
本文介绍几种兼容性:
- 源码:源码不兼容的变更会让原本能正常编译(没有错误或警告)的代码无法再编译
- 二进制:如果互换两个二进制构件不会导致加载或链接错误,就称它们是二进制兼容的
- 行为:如果同一个程序在应用变更前后表现出不同行为,就称该变更是行为不兼容的
请记住,这些定义仅针对纯 Kotlin。从其他语言(例如 Java)的视角来看 Kotlin 代码的兼容性不在本文讨论范围内。
不兼容的变更
构造函数实参相对于 调用的求值顺序
问题:KT-19532 组件:Kotlin/JVM 不兼容变更类型:行为 简要说明:1.3 中改变了相对于类初始化的求值顺序 弃用周期:- <1.3:旧行为(细节见该问题)- >= 1.3:行为已改变,可以使用
-Xnormalize-constructor-calls=disable暂时回退到 1.3 之前的行为。对该标志的支持将在下一个主要版本中移除。
注解构造函数参数上缺少以 getter 为目标的注解
问题:KT-25287 组件:Kotlin/JVM 不兼容变更类型:行为 简要说明:1.3 中注解构造函数参数上以 getter 为目标的注解会被正确写入 class 文件 弃用周期:- <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:当涉及类型推断时,编译器可能漏掉这类断言,从而在针对二进制构件编译时允许潜在的null传播(细节见该问题)。- >=1.3:编译器会生成此前漏掉的断言。这可能导致(错误地)在这里传入null的代码更早失败。-XXLanguage:-StrictJavaNullabilityAssertions可用于暂时恢复到 1.3 之前的行为。对该标志的支持将在下一个主要版本中移除。
枚举成员上不健全的智能转换
问题:KT-20772 组件:核心语言 不兼容变更类型:源码 简要说明:对某个枚举项成员的智能转换将只正确地应用于该枚举项 弃用周期:- <1.3:对某个枚举项成员的智能转换可能导致对其他枚举项中同一成员的不健全智能转换。- >=1.3:智能转换只会正确地应用于某个枚举项的成员。
-XXLanguage:-SoundSmartcastForEnumEntries会暂时恢复旧行为。对该标志的支持将在下一个主要版本中移除。
在 getter 中重新赋值 val 的幕后字段
问题:KT-16681 组件:核心语言 不兼容变更类型:源码 简要说明:现在禁止在其 getter 中重新赋值
val属性的幕后字段 弃用周期:- <1.2:Kotlin 编译器允许在其 getter 中修改val的幕后字段。这不仅违反 Kotlin 语义,还会生成重新赋值final字段的不良 JVM 字节码。- 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 起,禁止在枚举项中使用嵌套分类器(类、对象、接口、注解类、枚举类) 弃用周期:- <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:把弃用警告升级为错误
涉及带伴生对象的复杂类层级时的可见性规则
问题:KT-21515、KT-25333 组件:核心语言 不兼容变更类型:源码 简要说明:从 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 赋单个元素的语义,使赋数组等价于赋予数组的展开
target 为 EXPRESSION 的注解的保留策略
问题:KT-13762 组件:核心语言 不兼容变更类型:源码 简要说明:从 Kotlin 1.3 起,target 为
EXPRESSION的注解只允许使用SOURCE保留策略 弃用周期:- <1.2:允许 target 为EXPRESSION、保留策略不是SOURCE的注解,但在使用处会被静默忽略 - 1.2.X:对这类注解的声明报告弃用警告 - >=1.3:把警告升级为错误
target 为 PARAMETER 的注解不应适用于参数类型
问题:KT-9580 组件:核心语言 不兼容变更类型:源码 简要说明:从 Kotlin 1.3 起,当 target 为
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:这些构件已停止提供
标准库中的注解
问题:KT-21784 组件:kotlin-stdlib (JVM) 不兼容变更类型:二进制 简要说明:Kotlin 1.3 从标准库中移除
org.jetbrains.annotations包中的注解,并把它们移到随编译器一起提供的单独构件中:annotations-13.0.jar和mutability-annotations-compat.jar弃用周期:- <1.3:注解随标准库构件一起提供 - >=1.3:注解在单独的构件中提供