- 0.1:用于Kotlin功能和接口的SAM转换
- 0.2:更多用例中的自动推断
- 0.3:lambda的最后的表达式类型智能转换
- 0.4:可调用引用的智能转换
- 0.5:可调用引用更好地推断
- 0.6:委派属性更好地推断
- 0.7:废弃使用的实验协程
- 0.8:删除不推荐使用的mod运算符
- 0.9:弃用从浮动类型到Byte和Short的转换
- 0.10:通用反射API
- 0.11:use()和时间测量功能的新合同
- 0.12:用于Kotlin反射的Proguard配置
- 0.13:Gradle DSL变化
- 0.14:新的后端
- 0.15:将声明导出到JavaScript
- 0.16:默认情况下,Objective-C泛型支持
3月23日,Jetbrains发布了Kotlin 1.4的第一个预览版本,在本文中,将重点介绍1.4-M1中提供的新功能和关键改进:
- 默认情况下会启用功能更强大的新类型推断算法。
- Contracts现在可用于成员函数。
- Kotlin/JVM编译器现在在Java 8+目标的字节码中生成类型注释。
- 有一个针对Kotlin/JS的新后端,对产生的工件进行了重大改进。
- 标准库中的演化变化:完成弃用周期并弃用一些其他部件。
更强大的类型推断算法
Kotlin 1.4使用新的功能更强大的类型推断算法。 通过指定编译器选项,已经可以在Kotlin 1.3中尝试这种新算法,现在默认情况下使用它。 可以在YouTrack中找到新算法中已解决的问题的完整列表。
用于Kotlin功能和接口的SAM转换
当想使用一种“单一抽象方法”的接口时,SAM转换使你可以传递lambda。 以前,只能在使用Kotlin的Java方法和Java接口时应用SAM转换,现在也可以将其与Kotlin函数和接口一起使用。
Kotlin现在支持Kotlin接口的SAM转换。 注意,它的工作方式不同于Java:需要显式标记函数接口。 使用fun关键字标记接口后,只要将此类接口作为参数,就可以将lambda作为参数传递:
fun interface Action {
fun run()
}
fun runAction(a: Action) = a.run()
fun main() {
runAction {
println("Hello, Kotlin 1.4!")
}
}
Kotlin从一开始就支持Java接口的SAM转换,但是有一种情况不被支持。如果调用以两个SAM接口作为参数的Java方法,则两个参数都必须是lambda或常规对象。 不可能将一个参数作为lambda传递,将另一个参数作为对象传递。 新算法解决了这个问题,无论如何都可以传递lambda而不是SAM接口。
更多用例中的自动推断
新的推理算法在许多情况下会推断类型,在这些情况下,旧的推理需要明确指定它们。 例如,在下面的示例中,将lambda参数的类型正确推断为String ?:
val rulesMap: Map<String, (String?) -> Boolean> = mapOf(
"weak" to { it != null },
"medium" to { !it.isNullOrBlank() },
"strong" to { it != null && "^[a-zA-Z0-9]+$".toRegex().matches(it) }
)
在Kotlin 1.3中,需要引入一个显式的lambda参数,或将其替换为具有显式泛型参数的Pair构造函数以使其起作用。
lambda的最后的表达式类型智能转换
在Kotlin 1.3中,除非指定期望的类型,否则lambda内的最后一个表达式不是明智的强制转换。 因此,在以下示例中,Kotlin 1.3推断String? 作为结果变量的类型:
val result = run {
var str = currentValue()
if (str == null) {
str = "test"
}
str // Kotlin编译器知道str在这里不为null
}
// result在Kotlin 1.3中是String?,Kotlin 1.4中是String
在Kotlin 1.4中,由于使用了新的推断算法,lambda内部的最后一个表达式得到了智能转换,并且此新的更精确。 因此,结果变量的类型变为String。
在Kotlin 1.3中,通常需要添加显式强制转换(!!或键入as String之类的强制转换)以使这种情况起作用,现在这些强制转换已不再需要。
可调用引用的智能转换
在Kotlin 1.3中,无法访问智能投射类型的成员引用。 现在可以:
fun perform(animal: Animal) {
val kFunction: KFunction<*> = when (animal) {
is Cat -> animal::meow
is Dog -> animal::woof
}
kFunction.call()
}
在将Animal变量巧妙地强制转换为特定类型的Cat和Dog之后,可以使用不同的成员引用animal :: meow和animal :: woof。 在检查类型之后,可以访问与子类型相对应的成员引用。
可调用引用更好地推断
现在,使用具有默认参数值的函数的可调用引用更加方便。 例如,对以下foo函数的可调用引用可以解释为采用一个Int参数或不采用任何参数:
fun foo(i: Int = 0): String = "$i!"
fun apply1(func: () -> String): String = func()
fun apply2(func: (Int) -> String): String = func(42)
fun main() {
println(apply1(::foo))
println(apply2(::foo))
}
委派属性更好地推断
在分析by关键字后面的委托表达式时,未考虑委托属性的类型。 例如,以下代码以前不会编译过,但是现在编译器正确地将旧参数和新参数的类型推断为String ?:
import kotlin.properties.Delegates
fun main() {
var prop: String? by Delegates.observable(null) { p, old, new ->
println("$old → $new")
}
prop = "abc"
prop = "xyz"
}
Contracts支持
定义自定义合同的语法仍处于试验阶段,但是已经支持了一些新的合同可能有用的案例。
现在,可以使用修饰的泛型类型参数来定义合同。 例如,可以为assertIsInstance函数实现以下协定:
@OptIn(ExperimentalContracts::class)
inline fun <reified T> assertIsInstance(value: Any?) {
contract {
returns() implies (value is T)
}
assertTrue(value is T)
}
由于T类型参数是正则化的,因此可以在函数体中检查其类型。 现在,在合同中也可以这样做。 将带有断言消息的类似功能添加到kotlin.test库。
此外,现在可以为最终成员定义自定义合同。 以前,完全禁止为成员功能定义合同,因为在层次结构中的某些成员上定义合同意味着相应合同的层次结构,而这仍然是设计和讨论的问题。 但是,如果成员函数是最终函数且未覆盖任何其他函数,则可以安全地为其定义合同。
标准库更改
废弃使用的实验协程
在1.3.0版中,不推荐使用kotlin.coroutines.experimental API,而推荐使用kotlin.coroutines。 在1.4-M1中,我们通过从标准库中删除kotlin.coroutines.experimental完成了弃用周期。 对于仍然在JVM上使用它的用户,提供了一个兼容工件kotlin-coroutines-experimental-compat.jar与所有实验协程API。 已将其发布到Maven,并将其包含在标准库旁边的Kotlin发行版中。 当前,已将其与1.4-M1工件一起发布到了Bintray存储库中。
删除不推荐使用的mod运算符
另一个不建议使用的函数是数字类型的mod运算符,该运算符可计算除法运算后的余数。 在Kotlin 1.1中,它被rem()函数取代。 现在,已将其从标准库中完全删除。
弃用从浮动类型到Byte和Short的转换
标准库包含用于将浮点数转换为整数类型的函数:toInt(),toShort(),toByte()。 由于数值范围狭窄且变量大小较小,将浮点数转换为Short和Byte可能会导致意外结果。 为避免此类问题,从1.4-M1开始,不建议在Double和Float上使用函数toShort()和toByte()。 如果仍然需要将浮点数转换为Byte或Short,使用两步转换:转换为Int,然后转换为目标类型。
通用反射API
修改了通用反射API。 现在,它仅包含所有三个目标平台(JVM,JS,Native)上可用的成员,因此可以确保相同的代码可在其中任何一个上工作。
use()和时间测量功能的新合同
正在扩大标准库中合同的使用范围。 在1.4-M1中,添加了合同,声明对use()函数以及时间测量函数measureTimeMillis()和measureNanoTime()一次执行代码块。
用于Kotlin反射的Proguard配置
从1.4-M1开始,在kotlin-reflect.jar中嵌入了Kotlin Reflection的Proguard/R8配置。 有了这个功能,大多数使用R8或Proguard的Android项目都可以使用kotlin-reflect进行工作,而无需其他配置。 不再需要复制粘贴Kotlin反射相关信息到Proguard规则。 但是请注意,仍然需要明确列出要考虑的所有API。
Kotlin/JVM
从1.3.70版开始,Kotlin能够在JVM字节码(目标版本1.8+)中生成类型注释,以便它们在运行时可用。 社区要求此功能已有一段时间,因为它使使用某些现有Java库变得更加容易,并为新库的作者提供了更多功能。
在以下示例中,可以在字节码中发出String类型的@Foo批注,然后由库代码使用:
@Target(AnnotationTarget.TYPE)
annotation class Foo
class A {
fun foo(): @Foo String = "OK"
}
Kotlin/JS
对于Kotlin/JS,此里程碑包括对Gradle DSL的一些更改,并且它是第一个包含新的IR编译器后端的版本,该后端启用了优化和新功能。
Gradle DSL变化
在kotlin.js和多平台Gradle插件中,引入了新的重要设置。 在build.gradle.kts文件的目标块内,如果想在构建过程中生成.js工件,则可以使用并需要设置produceExecutable():
kotlin {
target {
useCommonJs()
produceExecutable()
browser {}
}
}
如果正在编写Kotlin/JS库,则可以省略produceExecutable()。 当使用新的IR编译器后端时,省略此设置意味着将不会生成可执行的JS文件(因此,构建过程将运行得更快)。 将在build/libs文件夹中生成一个klib文件,该文件可从其他Kotlin/JS项目使用,也可作为同一项目中的依赖项。 如果未明确指定produceExecutable(),则默认情况下会发生这种情况。
使用produceExecutable()将生成可从JavaScript生态系统执行的代码,无论是其自己的入口点还是作为JavaScript库。 这将生成实际的JavaScript文件,这些文件可以在节点解释器中运行,可以嵌入HTML页面中并在浏览器中执行,或用作JavaScript项目的依赖项。
请注意,以新的IR编译器后端为目标时,produceExecutable()将始终为每个目标生成一个独立的.js文件。 当前,不支持在多个生成的工件之间进行重复数据删除或拆分代码。 可以期望ProduceExecutable()中的此行为会在后续里程碑中发生变化。 此选项的命名将来也会更改。
新的后端
Kotlin 1.4-M1是第一个包含针对Kotlin/JS目标的新IR编译器后端的版本。 该后端是极大改进的基础,也是Kotlin/JS与JavaScript和TypeScript交互方式发生某些变化的决定性因素。 下面突出显示的功能均针对新的IR编译器后端。 虽然默认情况下尚未启用它,但我们建议在项目中进行尝试,开始为新的后端准备库。
要开始使用新的后端,请在gradle.properties文件中设置以下标志:
kotlin.js.compiler=ir
如果需要为IR编译器后端和默认后端生成库,则可以选择将此标志设置为两者。
kotlin.js.compiler=both
将声明导出到JavaScript
使用IR编译器后端时,标记为public的声明不再自动导出(甚至不包含名称混用的版本)。 这是因为IR编译器的封闭世界模型假定对导出的声明进行了特殊注释-这是有助于进行上述优化的因素之一。
要使顶级声明可从外部在JavaScript或TypeScript中使用,请使用@JsExport批注。 在下面的示例中,使KotlinGreeter(及其方法)和farewell()可以从JavaScript使用,但是仅对Kotlin保持secretGreeting():
package blogpost
@JsExport
class KotlinGreeter(private val who: String) {
fun greet() = "Hello, $who!"
}
@JsExport
fun farewell(who: String) = "Bye, $who!"
fun secretGreeting(who: String) = "Sup, $who!" // only from Kotlin!
Kotlin/Native
默认情况下,Objective-C泛型支持
Kotlin的早期版本为Objective-C互操作中的泛型提供了实验性支持。 要从Kotlin代码生成具有泛型的框架标头,必须使用-Xobjc-generics编译器选项。 在1.4-M1中,此行为成为默认行为。 在某些情况下,这可能会破坏现有的调用Kotlin框架的Objective-C或Swift代码。 要在不使用泛型的情况下编写框架标头,请添加-Xno-objc-generics编译器选项。
binaries.framework {
freeCompilerArgs += "-Xno-objc-generics"
}
使用新版本
可以在play.kotl.in上在线尝试Kotlin。
在IntelliJ IDEA和Android Studio中,可以将Kotlin插件更新为1.4-M1版本。
如果要处理在之前创建的现有项目安装预览版本,则需要在Gradle或Maven中为预览版本配置构建。
可以从Github发布页面下载命令行编译器。
可以使用此版本一起发布的库的以下版本:
- kotlinx.atomicfu 版本:0.14.2-1.4-M1
- kotlinx.coroutines 版本:1.3.5-1.4-M1
- kotlinx.serialization 版本: 0.20.0-1.4-M1
- ktor 版本: 1.3.2-1.4-M1