来为Kotlin 1.3.60正式版的发布而欢呼!除了质量的改进,下面是该版本的关键更新:
- 优化了
内联
类的比较行为 - 改进了测试工具,J2K转换器,Kotlin Gradle脚本编辑工具
- Kotlin/Native支持更多平台
- 改善了IDE对Kotlin/MPP的体验
- 对于Kotlin/JS,添加了源映射的,并改善平台测试执行器的集成
- 提供Kotlin1.4中已有实现的预览
你可以在更新日志中查阅完整的更新列表。我们仍然非常感谢所有外部的贡献者.
更多细节请让我娓娓道来。
语言的变化
小版本迭代不会带来语言的变更,除了小规模的改进(如改进令人迷惑的错误信息)或实验特性的更新(像内联
类)。如想提前尝鲜Kotlin1.4,请阅读相应的文档。
内联类的改进
对于内联
类的比较,其类以内的数据类型会有一次不必要的装箱行为。但自1.3.60版本开始,里面数据类型的比较得以优化:
每个内联
类的字节码都会生成一个特殊的静态方法equals-impl0
,用于比对类中的数据类型。当你在非装箱实例上进行比对时,其隐式调用会避免额外的装箱行为。
请注意,暂时无法为内联类复写equals/hashCode
方法。生成的equals-impl0
方法只是简单比较数据。在未来的版本,当允许内联类自定义equals
方法时,内部仍然会调用相同的equals-imp0
方法。
由于兼容性的原因,该优化只适用于Kotlin1.4版本的kotlin.Result
类。
错误信息的改进
在小部分的情况下,编译错误信息并不能明确地告知你错误发生的原因。我们尝试改善并减少会带来疑惑的错误信息。
Kotlin支持trailing lambda convention:lambda允许在括号之外。同样允许lambda另起一行。但当编译器认为新一行的大括号是函数的参数时,可能会产生歧义:

对于这种情况的错误信息现在得以改进了。你可以执行简单的自动修复方案:在上一行的末尾插入一个分号。(没错,Kotlin有些时候也是需要分号的!)
另一个值得强调的情况是针对可变惰性常量。惰性
变量设计上是只读的,但这设计可能会引起疑惑。现在其错误信息得以改进并提供了自动的修复捷径:

如果你遇到其他容易疑惑的用例,或你缺少部分非常有用的修复捷径,请在问题跟踪器提交这些请求。
IntelliJ IDEA支持
Scratches和Worksheets
我们重新设计并改良了Scratch文件,其目的是允许你以最小的代码库进行试验。现在可以在不同的窗口更方便地查看结果。涉及多行输出的结果会被包裹起来,同时高亮特定行。

有些时候Scratch文件并不能很好地执行。在如创建demo项目,展示特性等出于教学目的的场景下,你会更倾向于在项目内部而非外部去创建这些用例。对于这些需求,请使用Kotlin的Worksheets:

Kotlin Worksheets从概念上和技术上来说,与Scratch非常相似:你可以使用代码库并立即查看结果。两者之间的主要区别在于Worksheets是项目的一部分,这意味着可以通过VCS管理,而Scratches设计初衷是项目外的一部分。
build.gradle.kts
我们仍致力于提高你在编写Kotlin Gradle构建脚本的体验。着重强调的性能改善已经完成。我们会继续与Gradle紧密合作以不断改进。

Debugging改善
现在你可以在Kotlin函数中设置断点了。调试器会在进入或退出对应函数时暂停。你同样可以根据需求设置条件:

自动补全和导入的改善
几个已知的错误已经被修复了,如自动补全在包名和本地变量名重叠时的情况:

现在当你为枚举重新定义名称时,它的成员能在自动补全候选框中正常显示了:

如果你用到invoke
这样的可省略语法操作符,IntelliJ IDEA建议你使用自动导入:

新的Java-to-Kotlin转换器
我们为新的Java-to-Kotlin加入了很多优秀的设计。解决了很多特殊情况,例如,现在能正确分析并转换静态导入和各种用法的集合,集合将能正确转换成可变或只读,无论该集合是否为通用参数。
现在,当你一次转换多个文件时,它们将会被共同分析,每个文件的内容都会影响最后输出的结果。 例如,你将null
作为String
参数传递给Java中的foo
函数,则转换后的Kotlin函数参数是String?
:

新的转换器现在将被默认启用。
Eclipse IDE插件的更新
我们非常高兴地宣布kotlin-eclipse插件现已支持单个模块的实验性增量编译。在eclipse设置的Kotlin | Building
部分勾”incremental compilation”以尝试该功能。由于这是一个实验性特性,我们非常欢迎你的任何反馈!

Kotlin/Multiplatform
我们将重心放在MPP工具上了,若有和你的此前预期不一致的地方,请尝试新的改进!
尽管未来的正式版会在Kotlin的多平台方面带来更强大的功能,但当前版本着重改进/修复IDE的许多可用性问题。 尤其我们极大增强了一些“create expect
”快速修复的功能。
Kotlin/Native
Kotlin/Native编译器获得了很多新功能:
- 和最新工具的兼容性:XCode和LLVM 8.0
- 更多的新平台:
- watchOS
watchos_x86
watchos_arm64
watchos_arm32
- tvOS
tvos_x64
tvos_arm64
- Android (native)
android_x86
android_x64
- watchOS
- 针对正式版二进制文件的iOS崩溃报告的实验性symbolication(包括LLVM内联代码,比XCode可解码的代码更为先进)。
- 对Kotlin对象的Objective-C弱引用/共享引用的线程安全追踪。
- 支持对
suspend
callable的引用. - “无限”可变参数函数 (受限于JVM限制)
- work queue将能和任何上下文/线程关联,而不仅仅是通过
Worker.start
临时创建的上下文/线程。
通用Kotlin/Multiplatform命令行解析器
你们可能有人已经注意到kotlinx.cli
项目已经停更了几个月。 我们很高兴分享该项目,其中大部分的代码已被重写,并且它也包含在此版本的Kotlin/Native编译器中。
我们感谢早期新技术开荒用户的反馈! 你可以查看某些示例(俄罗斯方块, CSV解析器, 视频播放器)的用法,和内部的用法。
性能
尽管Kotlin/Native编译器还未对性能进行深度优化。但该正式版所带来的一些速度提升已足以让人惊讶。
对于大型项目,通过直接从klib(而不是源代码)生成本机库来提高了编译速度。
运行时性能也得到了改善:接口调用现在快了5倍,类型检查快了50倍!
修复
- 对于内联代码缺少debug所需要信息:部分内联优化会遗漏更新对于源码行号和实际字节码位置间的映射,这会导致设置断点的行为产生异常,导致超出目标位置的情况。我们已经提高了它们的记性,现在断点能正常执行了。
- 向可变参数传递
null
当向具有可变参数
的函数传递null
时,例如:platform.posix.printf(“%p”, null)编译器会因为无法判断实际的类型而崩溃。请注意,现在我们会将该值视为COpaquePointer
:无类型/ void指针,等效于C中的void *。 - 为iOS/macOS的负值拆箱有些时候,处理负数字节会导致严重的崩溃;这是LLVM上的一个BUG,我们建议通过手动的方案去解决。从当前正式版开始,这个解决方案由编译器自动执行。
Kotlin/JS
在Kotlin/JS领域,org.jetbrains.kotlin.js
新插件所带来的改进将能提升你的生活品质和简化你的工作。尤其是对源映射的支持和测试运行器的改进。
源映射
在Kotlin 1.3.60,可以通过org.jetbrains.kotlin.js
Gradle插件自动为以JavaScript为目标的代码生成源映射。当你遇到错误时它可以提供可供阅读的堆栈跟踪信息,这使你可以更轻松地调试自己的代码,断点,代码注释,局部变量等的支持,将是浏览器开发工具的黑科技。下一段落将介绍如何简化JS平台的测试工作。

测试运行器的改进
当你在JS平台上运行测试,生成的Gradle报告将包含常规通道的标准输出(即log
,warn
,error
)。该功能允许在Node.js或浏览器平台上启用。这与源映射的集成使测试的堆栈跟踪更易于阅读,并方便与代码结合分析——文件名和行号直接指向你的Kotlin源代码:

JS平台已支持测试过滤器,意味着可以有选择性地运行测试,而无需每次都得完整执行所有测试任务。你可以通过在Gradle命令行中指定--tests
标记使用该功能:
你也可以使用IntelliJ IDEA中的 gutter图标来运行单个测试或指定组合的测试:

Kotlin1.4即将到来的变化
Kotlin 1.4将计划在2020年发布。但你可以通过指定相应的语言版本来尝试一些已实现的特性:
请注意当前Kotlin1.4还在试验性状态
NPE断言
通过设置apiVersion
到1.4
,你可以看到此前介绍过的空检查优化。下面的代码将会抛出一个NullPointerException
,而非IllegalStateException
以及旧的错误信息"JavaCode.getNull() must not be null"
:
when
里的break和continue
Kotlin1.4其中一个语言层面上的变化是允许在when
里面使用break
和continue
了。当前禁止使用不带标签的break
和continue
,是因为这些关键字作为when
的fall-through的可能候选。 但是使用标签非常麻烦,因此break
和continue
能在外部存在循环下正确执行 :
when
的fall-through行为有待设计。
尾递归函数的变化
我们准备修复尾递归函数在”特殊情况”下的一些特定行为。
默认函数的初始化顺序
当你的尾递归函数定义了具有副作用的默认值时,这个更改才有意义。 在Kotlin 1.3中,tailrec
函数内的默认值的初始化顺序是错误的:默认值初始化顺序是从尾向头,即使反之亦然,从头向尾,这个规则同样适用于普通函数。
通过下面的例子可以明显看到区别:
在Kotlin1.3,输出是:
在Kotlin1.4,输出是:
我们希望实际情况中尽量不要采取这种写法。 (但如果由于某种原因你使用了这种复杂组合的语言特性,请注意这一即将到来的变化。)
在Kotlin 1.3中,结合使用open和tailrec修饰符是一个警告,在Kotlin 1.4中,它将成为错误。 请注意,这是一个“重大变化”:过去可以正常工作的代码不再起作用,但是我们不希望这种情况在实践中使用。
禁止同时使用open
tailrec
修饰函数
在Kotlin 1.3中,同时使用open
和tailrec
修饰符只是一个warning,但在Kotlin 1.4中,它将是一个error。 请注意,这是一个“有破坏性的变化”:过去可以正常工作的代码将报错,但是我们不建议在实际环境中采取如此写法。
目前尚无法明确open
尾递归函数的行为该以尾递归为主还是oepn
函数为主。 在Kotlin 1.3中,open
修饰符会被“忽略”,但这容易让人产生疑惑:
这段代码的输出是:
foo
的函数行为更倾向于尾递归函数的预期:它调用了自身并在底层执行了优化
当我们从A
的foo
函数中去除tailrec
修饰符,它的输出会变成:
现在foo
函数的行为更符合open
函数的预期:首先,super.foo(count)
显式调用父类A
的函数。然后foo(count-1)
是一个虚函数调用,实际调用的是子类B
中的函数。
由于类似的行为存在歧义,因此Kotlin1.4中禁止同时使用open
和tailrec
进行修饰。
如何更新
照旧,你可以在play.kotl.in在线试用Kotlin。
- Maven, Gradle, 和npm:编译器和标准库版本指定为
1.3.60
。这里查看文档。 - IntelliJ IDEA和Android Studio:打开Tools | Kotlin | Configure Kotlin Plugin Updates然后点击“Check for updates now”按钮,更新Kotlin插件到1.3.60版本。
- Eclipse:在Marketplace安装插件。
- 命令行编译器:在Github release page下载最新版本。
若你在新版本中发现任何问题,欢迎在论坛或 Slack (点击获取邀请)里寻求帮助,或者在问题跟踪器提交问题。
Let’s Kotlin!
其他贡献者
我们特别感谢Steven Schäfer,他优化了内联
类中的比较.
我们同样感谢PR被合并到该版本的其他贡献者:
- Toshiaki Kameyama
- Ivan Gavrilovic
- Mads Ager
- Mark Punzalan
- pyos
- Jake Wharton
- Yanis Batura
- ilgonmic
- Kristoffer Andersen
- Sebastian Schuberth
- Kevin Bierhoff
- scache
- keijumt
- Louis CAD
- Matthew Gharrity
- Jim Sproch
- Jim S
- Dereck Bridie
- Sascha Peilicke
- SatoShun
- Sergey Bogolepov
- Dat Trieu
- Burak Eregar
- Ty Smith
- Vladimir Krivosheev
- Alex Chmyr