13.6.9.4 Kotlin 1.5.x 兼容性指南

原文链接: https://kotlinlang.org/docs/compatibility-guide-15.html

13.6.9.4 Kotlin 1.5.x 兼容性指南

保持语言现代性 和 平滑升级 是 Kotlin 语言设计的基本原则。前者指出,阻碍语言演进的构造应当被移除;后者指出,这种移除应当事先充分沟通,以便代码迁移尽可能顺畅。

虽然大多数语言变更已经通过其他渠道公布(例如更新日志或编译器警告),本文仍然把它们全部汇总,为从 Kotlin 1.4 迁移到 Kotlin 1.5 提供完整参考。

基本术语

本文介绍几种兼容性:

  • 源码(source):源码不兼容的变更会让原本能正常编译(没有错误或警告)的代码无法再编译
  • 二进制(binary):如果互换两个二进制构件不会导致加载或链接错误,就称它们是二进制兼容的
  • 行为(behavioral):如果同一个程序在应用变更前后表现出不同行为,就称该变更是行为不兼容的

请记住,这些定义仅针对纯 Kotlin。从其他语言(例如 Java)的视角来看 Kotlin 代码的兼容性不在本文讨论范围内。

语言与标准库

禁止在签名多态调用中使用展开运算符

问题:KT-35226 组件:核心语言 不兼容变更类型:源码 简要说明:Kotlin 1.5 将禁止在签名多态调用中使用展开运算符(*) 弃用周期:- < 1.5:在调用处对存在问题的运算符引入警告 - >= 1.5:把该警告升级为错误,可以使用 -XXLanguage:-ProhibitSpreadOnSignaturePolymorphicCall 暂时回退到 1.5 之前的行为

禁止非抽象类包含从该类不可见的抽象成员(internal/包级私有)

问题:KT-27825 组件:核心语言 不兼容变更类型:源码 简要说明:Kotlin 1.5 将禁止非抽象类包含从该类不可见的抽象成员(internal/包级私有) 弃用周期:- < 1.5:对存在问题类引入警告 - >= 1.5:把该警告升级为错误,可以使用 -XXLanguage:-ProhibitInvisibleAbstractMethodsInSuperclasses 暂时回退到 1.5 之前的行为

禁止在 JVM 上把基于非具体化类型参数的数组用作具体化类型实参

问题:KT-31227 组件:核心语言 不兼容变更类型:源码 简要说明:Kotlin 1.5 将禁止在 JVM 上把基于非具体化类型参数的数组用作具体化类型实参 弃用周期:- < 1.5:对存在问题的调用引入警告 - >= 1.5:把该警告升级为错误,可以使用 -XXLanguage:-ProhibitNonReifiedArraysAsReifiedTypeArguments 暂时回退到 1.5 之前的行为

禁止不委托给主构造函数的枚举类次构造函数

问题:KT-35870 组件:核心语言 不兼容变更类型:源码 简要说明:Kotlin 1.5 将禁止不委托给主构造函数的枚举类次构造函数 弃用周期:- < 1.5:对存在问题的构造函数引入警告 - >= 1.5:把该警告升级为错误,可以使用 -XXLanguage:-RequiredPrimaryConstructorDelegationCallInEnums 暂时回退到 1.5 之前的行为

禁止从私有内联函数暴露匿名类型

问题:KT-33917 组件:核心语言 不兼容变更类型:源码 简要说明:Kotlin 1.5 将禁止从私有内联函数暴露匿名类型 弃用周期:- < 1.5:对存在问题的构造函数引入警告 - >= 1.5:把该警告升级为错误,可以使用 -XXLanguage:-ApproximateAnonymousReturnTypesInPrivateInlineFunctions 暂时回退到 1.5 之前的行为

禁止在带 SAM 转换的实参之后传入未展开的数组

问题:KT-35224 组件:核心语言 不兼容变更类型:源码 简要说明:Kotlin 1.5 将禁止在带 SAM 转换的实参之后传入未展开的数组 弃用周期:- 1.3.70:对存在问题的调用引入警告 - >= 1.5:把该警告升级为错误,可以使用 -XXLanguage:-ProhibitVarargAsArrayAfterSamArgument 暂时回退到 1.5 之前的行为

支持以下划线命名的 catch 块参数的特殊语义

问题:KT-31567 组件:核心语言 不兼容变更类型:源码 简要说明:Kotlin 1.5 将禁止引用用于在 catch 块中省略异常参数名的下划线符号(_) 弃用周期:- 1.4.20:对存在问题的引用引入警告 - >= 1.5:把该警告升级为错误,可以使用 -XXLanguage:-ForbidReferencingToUnderscoreNamedParameterOfCatchBlock 暂时回退到 1.5 之前的行为

把 SAM 转换的实现策略从基于匿名类改为 invokedynamic

问题:KT-44912 组件:Kotlin/JVM 不兼容变更类型:行为 简要说明:从 Kotlin 1.5 起,SAM(单一抽象方法)转换的实现策略将从生成匿名类改为使用 invokedynamic JVM 指令 弃用周期:- 1.5:改变 SAM 转换的实现策略,可以使用 -Xsam-conversions=class 把实现方式还原为之前使用的方案

基于 JVM IR 的后端的性能问题

问题:KT-48233 组件:Kotlin/JVM 不兼容变更类型:行为 简要说明:Kotlin 1.5 默认对 Kotlin/JVM 编译器使用基于 IR 的后端。对于较早的语言版本,默认仍使用旧后端。在 Kotlin 1.5 中使用新编译器时,你可能会遇到一些性能下降问题。我们正在努力修复这些情况。 弃用周期:- < 1.5:默认使用旧的 JVM 后端 - >= 1.5:默认使用基于 IR 的后端。如果你需要在 Kotlin 1.5 中使用旧后端,请在项目的配置文件中加入以下内容,以暂时回退到 1.5 之前的行为:在 Gradle 中: kotlin tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile> { kotlinOptions.useOldBackend = true } groovy tasks.withType(org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile) { kotlinOptions.useOldBackend = true } 在 Maven 中: xml <configuration> <args> <arg>-Xuse-old-backend</arg> </args> </configuration> 对该标志的支持将在未来的某个版本中移除。

基于 JVM IR 的后端中新的字段排序

问题:KT-46378 组件:Kotlin/JVM 不兼容变更类型:行为 简要说明:从 1.5 版起,Kotlin 使用基于 IR 的后端,它对 JVM 字节码的排序方式不同:它把在构造函数中声明的字段排在在类体中声明的字段之前,而旧后端则相反。新的排序可能改变那些依赖字段顺序的序列化框架(例如 Java 序列化)所影响程序的行为。 弃用周期:- < 1.5:默认使用旧的 JVM 后端。它把在类体中声明的字段排在构造函数中声明的字段之前。- >= 1.5:默认使用新的基于 IR 的后端。在构造函数中声明的字段会被生成在类体中声明的字段之前。作为变通办法,你可以在 Kotlin 1.5 中暂时切换到旧后端。为此,请在项目的配置文件中加入以下内容:在 Gradle 中: kotlin tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile> { kotlinOptions.useOldBackend = true } groovy tasks.withType(org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile) { kotlinOptions.useOldBackend = true } 在 Maven 中: xml <configuration> <args> <arg>-Xuse-old-backend</arg> </args> </configuration> 对该标志的支持将在未来的某个版本中移除。

为委托表达式含泛型调用的委托属性生成可空性断言

问题:KT-44304 组件:Kotlin/JVM 不兼容变更类型:行为 简要说明:从 Kotlin 1.5 起,Kotlin 编译器会为委托表达式中含泛型调用的委托属性生成可空性断言 弃用周期:- 1.5:为委托属性生成可空性断言(细节见该问题),可以使用 -Xuse-old-backend 或 -language-version 1.4 暂时回退到 1.5 之前的行为

把带 @OnlyInputTypes 注解类型参数的调用由警告升级为错误

问题:KT-45861 组件:核心语言 不兼容变更类型:源码 简要说明:Kotlin 1.5 将禁止像 contains、indexOf 和 assertEquals 这样传入无意义实参的调用,以提升类型安全 弃用周期:- 1.4.0:对存在问题的构造函数引入警告 - >= 1.5:把该警告升级为错误,可以使用 -XXLanguage:-StrictOnlyInputTypesChecks 暂时回退到 1.5 之前的行为

在带具名 vararg 的调用中使用正确的实参求值顺序

问题:KT-17691 组件:Kotlin/JVM 不兼容变更类型:行为 简要说明:Kotlin 1.5 将改变带具名 vararg 的调用中实参的求值顺序 弃用周期:- < 1.5:对存在问题的构造函数引入警告 - >= 1.5:把该警告升级为错误,可以使用 -XXLanguage:-UseCorrectExecutionOrderForVarargArguments 暂时回退到 1.5 之前的行为

在运算符函数调用中使用参数的默认值

问题:KT-42064 组件:Kotlin/JVM 不兼容变更类型:行为 简要说明:Kotlin 1.5 将在运算符调用中使用参数的默认值 弃用周期:- < 1.5:旧行为(细节见该问题)- >= 1.5:行为已改变,可以使用 -XXLanguage:-JvmIrEnabledByDefault 暂时回退到 1.5 之前的行为

当普通数列为空时,在 for 循环中产生空的反向数列

问题:KT-42533 组件:Kotlin/JVM 不兼容变更类型:行为 简要说明:当普通数列也为空时,Kotlin 1.5 会在 for 循环中产生空的反向数列 弃用周期:- < 1.5:旧行为(细节见该问题)- >= 1.5:行为已改变,可以使用 -XXLanguage:-JvmIrEnabledByDefault 暂时回退到 1.5 之前的行为

理顺 Char 到码点、Char 到数字的转换

问题:KT-23451 组件:kotlin-stdlib 不兼容变更类型:源码 简要说明:从 Kotlin 1.5 起,Char 到数值类型的转换将被弃用 弃用周期:- 1.5:弃用 Char.toInt()/toShort()/toLong()/toByte()/toDouble()/toFloat() 以及 Long.toChar() 这类反向函数,并提出替代方案

kotlin.text 函数中字符大小写不敏感比较的不一致

问题:KT-45496 组件:kotlin-stdlib 不兼容变更类型:行为 简要说明:从 Kotlin 1.5 起,Char.equals 在大小写不敏感场景下会改进:先比较字符的大写形式是否相等,再比较这些大写形式的小写形式(而不是字符本身)是否相等 弃用周期:- < 1.5:旧行为(细节见该问题)- 1.5:改变 Char.equals 函数的行为

移除默认的受区域设置影响的大小写转换 API

问题:KT-43023 组件:kotlin-stdlib 不兼容变更类型:源码 简要说明:从 Kotlin 1.5 起,String.toUpperCase() 这类默认受区域设置影响的大小写转换函数将被弃用 弃用周期:- 1.5:弃用使用默认区域设置的大小写转换函数(细节见该问题),并提出替代方案

逐步把集合 min 与 max 函数的返回类型改为非空

问题:KT-38854 组件:kotlin-stdlib (JVM) 不兼容变更类型:源码 简要说明:集合 min 和 max 函数的返回类型将在 1.6 中改为非空 弃用周期:- 1.4:引入 ...OrNull 函数作为同义函数,并弃用受影响的 API(细节见该问题)- 1.5.0:把受影响 API 的弃用级别提升为错误 - >=1.6:重新引入受影响的 API,但返回类型为非空

提高浮点类型到 Short 与 Byte 转换的弃用级别

问题:KT-30360 组件:kotlin-stdlib (JVM) 不兼容变更类型:源码 简要说明:在 Kotlin 1.4 中以 WARNING 级别弃用的、浮点类型到 Short 和 Byte 的转换,从 Kotlin 1.5.0 起将导致错误。 弃用周期:- 1.4:弃用 Double.toShort()/toByte() 和 Float.toShort()/toByte(),并提出替代方案 - 1.5.0:把弃用级别提升为错误

工具

不要在一个项目中混用多个 kotlin-test 的 JVM 变体

问题:KT-40225 组件:Gradle 不兼容变更类型:行为 简要说明:如果其中一个变体是由传递依赖引入的,项目中可能同时存在多个相互排斥的、面向不同测试框架的 kotlin-test 变体。从 1.5.0 起,Gradle 将不再允许同时存在相互排斥的、面向不同测试框架的 kotlin-test 变体。 弃用周期:- < 1.5:允许同时存在多个相互排斥的、面向不同测试框架的 kotlin-test 变体 - >= 1.5:行为已改变,Gradle 会抛出类似"Cannot select module with conflict on capability…“的异常。可能的解决办法:* 使用与传递依赖所引入的相同 kotlin-test 变体及相应的测试框架。* 找到该依赖的另一个不会传递引入该 kotlin-test 变体的版本,这样你就可以使用自己想用的测试框架。* 找到该依赖的另一个会传递引入其他 kotlin-test 变体的版本,且该变体使用你想用的测试框架。* 排除被传递引入的测试框架。下面的例子演示如何排除 JUnit 4:groovy configurations { testImplementation.get().exclude("org.jetbrains.kotlin", "kotlin-test-junit") } 排除该测试框架后,请测试你的应用。如果它无法正常工作,请回滚排除相关的改动,改用与该库相同的测试框架,并排除你自己的测试框架。