协程
经过长时间的测试,协程发布了稳定版本, 这意味着从Kotlin 1.3
开始,语言支持和API完全稳定。 协程概述页面。
Kotlin 1.3
在Reflection API
中引入了有关suspend-functions
和协程支持的可调用引用。
Kotlin/Native
Kotlin 1.3
继续改进和完善Native
项目,详细介绍。
多平台项目
在Kotlin 1.3
中,已经完全重新设计了多平台项目的模型,以提高表达能力和灵活性,使得共享通用代码更加容易。 此外,现在还支持Kotlin/Native
作为目标之一!
与旧模型的主要区别在于:
- 在旧模型中,需要将通用代码和特定于平台的代码放置在单独的模块中,并按预期的依赖关系进行链接。 现在,将通用的和特定于平台的代码放在同一模块的不同源根目录中,从而使项目更易于配置。
- 现在,存在针对不同支持平台的大量预设平台配置。
- 依赖项配置已更改;现在为每个源根分别指定依赖项。
- 现在可以在平台的任意子集之间共享源集(例如,在针对JS,Android和iOS的模块中,您可以拥有仅在Android和iOS之间共享的源集)。
- 现在支持发布多平台库。
有关更多信息,参阅Multiplatform Programming文档。
Contracts
Kotlin
编译器会进行大量的静态分析,以提供警告并减少样板。 最著名的功能之一具有基于执行的类型检查自动执行强制转换的功能:
fun foo(s: String?) { if (s != null) s.length // 编译器自动将's'强制转换为'String'}
但是,一旦在单独的方法中进行了这些检查,所有智能转换就会消失:
fun String?.isNotNull(): Boolean = this != nullfun foo(s: String?) { if (s.isNotNull()) s.length // 不会智能转换 :(}
为了改善这种情况下的行为,Kotlin 1.3
引入了称为Contracts
的实验机制。Contracts
允许函数以编译器可以理解的方式显式描述其行为。 当前,支持两种情况:
- 通过声明函数的调用结果与传递的参数值之间的关系来改进智能分析:
fun require(condition: Boolean) { // 这是一种语法形式,它告诉编译器: // 如果此函数返回成功,则传递的'condition'为true contract { returns() implies condition } if (!condition) throw IllegalArgumentException(...)}fun foo(s: String?) { require(s is String) // s在此处智能转换为String,否则require将会抛出一个异常}
- 在存在高阶函数的情况下改进变量初始化分析:
fun synchronize(lock: Any?, block: () -> Unit) { // 它将告诉编译器: // 此函数将在现在仅一次调用block contract { callsInPlace(block, EXACTLY_ONCE) }}fun foo() { val x: Int synchronize(lock) { x = 42 // 编译器知道传递给'synchronize'的lambda只会被调用一次,因此不会报告重新分配 } println(x) // 编译器知道lambda将绝对会被调用,并执行初始化,因此此处将x视为已初始化}
stdlib
已经可以使用Contracts
,这可以改善上述分析。 Contracts
的这部分内容是稳定的,这意味着可以立即从改进的分析中受益,而无需任何其他选择:
fun bar(x: String?) { if (!x.isNullOrEmpty()) { println("length of '$x' is ${x.length}") // 自动转换为非null }}
可以为自己的函数声明Contracts
,但是此功能是实验性的,因为当前语法处于早期原型状态,并且很可能会更改。 另外,当前Kotlin
编译器并不会验证Contracts
,因此编写正确和合理的Contracts
是程序员的责任。
when表达式捕获变量
在Kotlin 1.3
中,现在可以将when
捕获为变量:
fun Request.getBody() = when (val response = executeRequest()) { is Success -> response.body is HttpError -> throw HttpException(response.status) }
虽然已经可以在when
之前提取变量,但是在when
中val
的范围已适当地限制为when
的主体,因此可以防止名称空间污染。
可以在接口的伴生对象中声明@JvmStatic和@JvmField
使用Kotlin 1.3
,可以使用@JvmStatic
和@JvmField
标记来标记接口的伴随对象的成员。 在类文件中,此类成员将被提升到相应的接口并标记为静态。
例如以下Kotlin
代码:
interface Foo { companion object { @JvmField val answer: Int = 42 @JvmStatic fun sayHello() { println("Hello, world!") } }}
相当于以下Java
代码:
interface Foo { public static int answer = 42; public static void sayHello() { // ... }}
注解类中的嵌套声明
在Kotlin 1.3
中,注解类可以具有嵌套的类,接口,对象和伴生对象:
annotation class Foo { enum class Direction { UP, DOWN, LEFT, RIGHT } annotation class Bar companion object { fun foo(): Int = 42 val bar: Int = 42 }}
Function范型参数
在Kotlin
中,函数类型表示为具有不同数量参数的泛型类:Function0<R>
,Function1 <P0,R>
,Function2 <P0,P1,R>
等。 ,当前以Function22
结尾。
Kotlin 1.3
放宽了此限制,并增加了对Function
更大支持的支持:
fun trueEnterpriseComesToKotlin(block: (Any, Any, ... /* 42 more */, Any) -> Any) { block(Any(), Any(), ..., Any())}
内联类
内联类仅自Kotlin 1.3起可用,并且目前处于实验阶段。
Kotlin 1.3
引入了一种新的声明—inline class
。 可以将内联类视为常规类的受限版本,特别是,内联类必须具有一个属性:
inline class Name(val s: String)
Kotlin
编译器将使用此限制来积极优化内联类的运行时表示形式,并用基础属性的值替换其实例,并在可能的情况下删除构造函数调用,GC
压力并启用其他优化:
fun main() { // 在下一行中,没有构造函数调用发生,并且在运行时name仅包含字符串Kotlin val name = Name("Kotlin") println(name.s) }
无符号整数
无符号整数仅自
Kotlin 1.3
起可用,并且目前处于实验阶段
Kotlin 1.3引入了无符号整数类型:
kotlin.UByte
: 一个无符号的8-bit整数,范围从0到255kotlin.UShort
: 一个无符号的16-bit整数, 范围从0到65535kotlin.UInt
:一个无符号的32-bit整数, 范围从0到2^32 - 1kotlin.ULong
:一个无符号的64-bit整数, 范围从0到2^64 - 1
// 可以使用文字后缀来定义无符号类型val uint = 42u val ulong = 42uLval ubyte: UByte = 255u// 可以通过stdlib扩展将有符号类型转换为无符号类型,反之亦然:val int = uint.toInt()val byte = ubyte.toByte()val ulong2 = byte.toULong()// 无符号类型支持类似的运算符:val x = 20u + 22uval y = 1u shl 8val z = "128".toUByte()val range = 1u..5u
@JvmDefault
@JvmDefault
仅自Kotlin 1.3
起可用,目前处于实验阶段
Kotlin
面向广泛的Java
版本,包括Java 6
和Java 7
,其中接口不允许default
方法。 为方便起见,Kotlin
编译器可解决该限制,但该解决方法与Java 8
中引入的default
方法不兼容。
这是Java
互操作性的问题,因此Kotlin 1.3
引入了@JvmDefault
注解。 带有此注解的方法将作为JVM
的default
方法生成:
interface Foo { // 生成将作为带default关键字的方法 @JvmDefault fun foo(): Int = 42}
用
@JvmDefault
注解API
对二进制兼容性有严重的影响。 在生产环境中使用@JvmDefault
之前,请确保仔细阅读参考页。
标准库
多平台Random
在Kotlin 1.3
之前的版本中,没有统一的方法在所有平台上生成随机数-不得不求助于平台特定的解决方案,例如JVM
上的java.util.Random
。 此版本通过引入类kotlin.random.Random
来解决此问题,该类在所有平台上都可用:
val number = Random.nextInt(42) // number is in range [0, limit)println(number)
isNullOrEmpty/orEmpty扩展
stdlib
中已经存在某些类型的isNullOrEmpty
和orEmpty
扩展。 如果接收者为null
或为空,则第一个返回true
;如果接收器为null
,则第二个返回为空实例。 Kotlin 1.3
在对象的List
、Map
和Array
上提供了类似的扩展。
在两个现有数组之间复制元素
现有数组类型(包括无符号数组)的array.copyInto(targetArray,targetOffset,startIndex,endIndex)
函数使在纯Kotlin
中更容易实现:
val sourceArr = arrayOf("k", "o", "t", "l", "i", "n")val targetArr = sourceArr.copyInto(arrayOfNulls<String>(6), 3, startIndex = 3, endIndex = 6)println(targetArr.contentToString())sourceArr.copyInto(targetArr, startIndex = 0, endIndex = 3)println(targetArr.contentToString())
associateWith
拥有键的List
并想通过将这些键中的每个键与某个值相关联来构建映射是一种很常见的情况。 以前可以通过associate { it to getValue(it) }
函数来做到这一点,但是现在引入了一种更高效,更容易探索的替代方法:keys.associateWith { getValue(it) }
:
val keys = 'a'..'f'val map = keys.associateWith { it.toString().repeat(5).capitalize() }map.forEach { println(it) }
ifEmpty和ifBlank函数
List
、Map
、对象数组、char
序列和序列现在具有ifEmpty
函数,该函数允许指定一个后备值,如果该值是空的,它将代替接收者使用:
fun printAllUppercase(data: List<String>) { val result = data .filter { it.all { c -> c.isUpperCase() } } .ifEmpty { listOf("<no uppercase>") } result.forEach { println(it) }}printAllUppercase(listOf("foo", "Bar"))printAllUppercase(listOf("FOO", "BAR"))
Char
序列和字符串还具有if Blank
扩展名,该扩展名与Empty
的作用相同,但检查字符串是否全部为空格而不是空:
val s = " \n"println(s.ifBlank { "<blank>" })println(s.ifBlank { null })
一些小的改变
Boolean
类型有伴生对象Any?.hashCode()
扩展函数,如果为null
返回0
Char
现在提供了MIN_VALUE
/MAX_VALUE
常量- 基本类型伴生对象中的
SIZE_BYTES
和SIZE_BITS
常量。
工具
IDE中的代码样式支持
Kotlin 1.3
引入了对IDE
中推荐代码风格的支持。
kotlinx.serialization
kotlinx.serialization
是一个库,为Kotlin
中的对象(反序列化)提供多平台支持。 以前,这是一个单独的项目,但是从Kotlin1.3
开始,它与Kotlin
编译器发行版一起发布,与其他编译器插件同等。
脚本更新
请注意,脚本编写是一项实验性功能,这意味着未提供API的兼容性保证。
Kotlin 1.3
继续发展和改进脚本API
,引入了一些实验性的脚本自定义支持,例如添加外部属性,提供静态或动态依赖关系等。
Scratch支持
Kotlin 1.3
引入了对可运行Kotlin
暂存文件的支持。 Scratch文件是带有.kts
扩展名的kotlin
脚本文件,可以直接在编辑器中运行并获取结果。