Kotlin 1.3.x 兼容性指南
“保持语言现代性”与“舒适更新”是 Kotlin 语言设计的基本原则。前者指出应当移除阻碍语言演进的结构,后者则指出这种移除应当事先进行充分沟通,以使代码迁移尽可能顺畅。
虽然大多数语言变更已经通过更新日志或编译器警告等其他渠道公布,但本文档对这些变更进行了汇总,为从 Kotlin 1.2 迁移到 Kotlin 1.3 提供完整的参考。
基本术语
在本文档中,我们介绍了以下几种兼容性:
- 源码(Source):源码不兼容变更会导致过去可以正常编译(无错误或警告)的代码不再能够编译
- 二进制(Binary):如果两个二进制构件互相替换后不会导致加载或链接错误,则称它们是二进制兼容的
- 行为(Behavioral):如果同一个程序在应用变更前后的表现不同,则称该变更为行为不兼容
请记住,这些定义仅针对纯 Kotlin 而言。Kotlin 代码从其他语言角度(例如从 Java)来看的兼容性不在本文档的讨论范围之内。
不兼容变更
关于 <clinit> 调用的构造函数参数求值顺序
问题:KT-19532
组件:Kotlin/JVM
不兼容变更类型:行为
简要总结:在 1.3 中,相对于类初始化的求值顺序发生了变化
弃用周期:
- <1.3:旧行为(详见 Issue)
- >= 1.3:行为已更改,
-Xnormalize-constructor-calls=disable可用于暂时恢复到 1.3 之前的行为。对该标志的支持将在下一个主要版本中移除。
注解构造函数参数上缺失的以 getter 为目标的注解
问题:KT-25287
组件:Kotlin/JVM
不兼容变更类型:行为
简要总结:在 1.3 中,注解构造函数参数上以 getter 为目标的注解将被正确写入类文件
弃用周期:
- <1.3:注解构造函数参数上以 getter 为目标的注解未生效
- >=1.3:注解构造函数参数上以 getter 为目标的注解已正确生效并写入生成的代码中
类构造函数 @get: 注解中缺失的错误报告
问题:KT-19628
组件:核心语言
不兼容变更类型:源码
简要总结:在 1.3 中,以 getter 为目标的注解中的错误将得到正确报告
弃用周期:
- <1.2:未报告以 getter 为目标的注解中的编译错误,导致错误的代码也能正常编译。
- 1.2.x:错误仅由工具报告,编译器仍可在没有任何警告的情况下编译此类代码
- >=1.3:编译器也会报告错误,导致错误的代码被拒绝编译
对带有 @NotNull 注解的 Java 类型进行访问时的为 null 性断言
问题:KT-20830
组件:Kotlin/JVM
不兼容变更类型:行为
简要总结:对于带有非空注解的 Java 类型,其为 null 性断言的生成将更加激进,导致在此处传递
null的代码会更快失败。弃用周期:
- <1.3:当涉及类型推断时,编译器可能会遗漏此类断言,从而允许在针对二进制文件编译期间发生潜在的
null传播(详见 Issue)。- >=1.3:编译器会生成遗漏的断言。这可能会导致(错误地)在此处传递
null的代码更快失败。-XXLanguage:-StrictJavaNullabilityAssertions可用于暂时恢复到 1.3 之前的行为。对该标志的支持将在下一个主要版本中移除。
枚举成员上不完善的智能转换
问题:KT-20772
组件:核心语言
不兼容变更类型:源码
简要总结:对某个枚举条目成员的智能转换将仅正确应用于该枚举条目
弃用周期:
- <1.3:对某个枚举条目成员的智能转换可能会导致对其他枚举条目的相同成员进行不完善的智能转换。
- >=1.3:智能转换将仅正确应用于该枚举条目的成员。
-XXLanguage:-SoundSmartcastForEnumEntries将暂时恢复旧行为。对该标志的支持将在下一个主要版本中移除。
在 getter 中对 val 支持字段进行重新赋值
问题:KT-16681
组件:核心语言
不兼容变更类型:源码
简要总结:现在禁止在
val属性的 getter 中对其支持字段进行重新赋值弃用周期:
- <1.2:Kotlin 编译器允许在
val的 getter 中修改其支持字段。这不仅违反了 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:弃用警告提升为错误
涉及伴生对象与复杂类层次结构的可见性规则
组件:核心语言
不兼容变更类型:源码
简要总结:自 Kotlin 1.3 起,对于涉及伴生对象和嵌套分类器的复杂类层次结构,通过短名称访问的可见性规则更加严格。
弃用周期:
- <1.2:旧的可见性规则(详见 Issue)
- 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及类似方法在长序列中不会溢出。受影响方法的完整列表请参见 Issue。弃用周期:
- <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-stdlibkotlin-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:注解在独立的构件中提供
