Kotlin 1.1 新特性

新特性列表

-协程 -其它语言特性 -标准库 -JVM 后端 -JavaScript 后端

JavaScript

从 Kotlin 1.1 开始,JavaScript 支持不再是实验性的了。所有特性均支持,并为前端开发环境提过了大量新的工具。参看下文了解详细改变

协程

Kotlin 1.1关键的新特性就是协程,带来了像 async/wait,yield 这样的编程模式。Kotlin 设计的关键特性是所有协程都是由库实现的,而不是语言。所以你不需要与任何特定的编程范例或者并行库进行绑定。

协程是一个高效轻量级的线程,可以挂起并稍后恢复执行。协程是又挂起函数支持的:调用这个函数可能会挂起一个协程,并开启一个新的协程,大多数情况下采用匿名挂起函数(也就是可挂起lambda表达式)

让我们看一下在kotlinx.coroutines库中实现的async/await:

  1. // 在后台线程池中运行代码
  2. fun asyncOverlay() = async(CommonPool) {
  3. // 开启两个异步操作
  4. val original = asyncLoadImage("original")
  5. val overlay = asyncLoadImage("overlay")
  6. // and then apply overlay to both results
  7. applyOverlay(original.await(), overlay.await())
  8. }
  9. // 在 UI 上下文中中开启新的协程
  10. launch(UI) {
  11. // wait for async overlay to complete
  12. val image = asyncOverlay().await()
  13. // and then show it in UI
  14. showImage(image)
  15. }

async{...} 开启协程,当调用 await() 时协程被挂起,等待 original 和 overlay 的执行结果,当两者完成时恢复执行。

标准库用协程支持 lazily generated sequences 懒生成序列,主要使用 yieldyieldAll 函数。在这样的队列中,返回序列会在每次元素被取出后暂停,并在下次取元素是恢复,例子:

  1. val seq = buildSequence {
  2. for (i in 1..5) {
  3. // yield a square of i
  4. yield(i * i)
  5. }
  6. // yield a range
  7. yieldAll(26..28)
  8. }
  9. // print the sequence
  10. println(seq.toList())

更多信息请参看 coroutine documentation 以及 tutorials

其它语言特性

类型别名

类型别名允许给现有的类型定义别名。主要在集合,函数类型中很常用。例子:

  1. typealias OscarWinners = Map<String, String>
  2. fun countLaLaLand(oscarWinners: OscarWinners) =
  3. oscarWinners.count { it.value.contains("La La Land") }
  4. // Note that the type names (initial and the type alias) are interchangeable:
  5. fun checkLaLaLandIsTheBestMovie(oscarWinners: Map<String, String>) =
  6. oscarWinners["Best picture"] == "La La Land"

参看documentationKEEP 了解更多详细信息。

Bound callable references(绑定可执行引用 暂时没想好怎么翻译,如果你有好的建议请发issue)

使用 :: 操作符可以获取一个指向特定对象实例的方法或者熟悉的成员引用。之前这只能用在 lambda 表达式上。例子:

  1. val numberRegex = "\\d+".toRegex()
  2. val numbers = listOf("abc", "123", "456").filter(numberRegex::matches)

参看documentationKEEP 了解更多详细信息。

密封类和数据类

Kotlin 1.1 移除了一些密封类和数据类的限制。现在能在同一个文件中定义顶级密封类类的子类,而不必是内嵌类或者密封类类。数据类现在可以扩展其它类。这样可以更优雅简洁的定义表达式类的等级:

  1. sealed class Expr
  2. data class Const(val number: Double) : Expr()
  3. data class Sum(val e1: Expr, val e2: Expr) : Expr()
  4. object NotANumber : Expr()
  5. fun eval(expr: Expr): Double = when (expr) {
  6. is Const -> expr.number
  7. is Sum -> eval(expr.e1) + eval(expr.e2)
  8. NotANumber -> Double.NaN
  9. }
  10. val e = eval(Sum(Const(1.0), Const(2.0)))

参看 documentation or sealed classdata class KEEPs 获取更详细的信息。

lambdas 表达式的解构

现在可以使用destucting declaration 语法取出 lambda 中的参数:

  1. val map = mapOf(1 to "one", 2 to "two")
  2. // before
  3. println(map.mapValues { entry ->
  4. val (key, value) = entry
  5. "$key -> $value!"
  6. })
  7. // now
  8. println(map.mapValues { (key, value) -> "$key -> $value!" })

参看documentationKEEP 获取更详细的信息。

用下划线标注下未使用的参数

对于有多个参数的 lambda ,你可以用 _ 字符代替你不使用的参数。

  1. map.forEach { _, value -> println("$value!") }

destructuring declarations 中也可以用

  1. val (_, status) = getResult()

阅读KEEP获取更详细信息

数字值中的下划线

像 java8 一样 Kotlin 现在支持在数字值中使用下划线划分组:

  1. val oneMillion = 1_000_000
  2. val hexBytes = 0xFF_EC_DE_5E
  3. val bytes = 0b11010010_01101001_10010100_10010010

阅读[KEEP获取更详细信息

属性简写

用表达式定义个 get 属性,现在可以省略属性类型了:

  1. data class Person(val name: String, val age: Int){
  2. val isAdult get() = age >= 20 // 属性类型被推断为 'Boolean'
  3. }

内联属性访问器

现在没有 backing field 的属性访问器可以用 ‘inline’ 修饰。

这些属性访问器和内联函数的编译方式是一样的。

  1. public val <T> List<T>.lastIndex: Int
  2. inline get() = this.size -1

documentationKEEP 了解更多详细信息。

本地代理属性

现在可以在本地变量上使用 代理属性 了。一个应用场景就是定义一个懒求值的本地变量:

  1. val answer by lazy {
  2. println("Calculating the answer...")
  3. 42
  4. }
  5. if (needAnswer()) { // returns the random value
  6. println("The answer is $answer.") // answer is calculated at this point
  7. }
  8. else {
  9. println("Sometimes no answer is the answer...")
  10. }

阅读 KEEP 了解更多详细信息。

拦截代理属性绑定

代理属性现在可以通过 ‘provideDelegate’ 运算符拦截属性绑定。比如,在绑定属性名之前,想要检查属性名字,可以像下面这样做:

  1. class ResourceLoader<T>(id: ResourceID<T>) {
  2. operator fun provideDelegate(thisRef: MyUI, prop: KProperty<*>): ReadOnlyProperty<MyUI, T> {
  3. checkProperty(thisRef, prop.name)
  4. ... // property creation
  5. }
  6. private fun checkProperty(thisRef: MyUI, name: String) { ... }
  7. }
  8. fun <T> bindResource(id: ResourceID<T>): ResourceLoader<T> { ... }
  9. class MyUI {
  10. val image by bindResource(ResourceID.image_id)
  11. val text by bindResource(ResourceID.text_id)
  12. }

‘provideDelegate’ 方法可以可以在创建 MyUI 实例的每个属性时调用,并做相应的验证检查。

通用枚举值的访问

现在可以用通用方法罗列枚举类的所有值。

```kotlin enum class RGB { RED, GREEN, BLUE }

inline fun > printAllValues() { print(enumValues().joinToString { it.name }) }

标准库

string to number 的转换

在 String 类新增了转为数字而不抛出异常的扩展: String.toIntOrNull(): Int?, String.toDoubleOrNull(): Double? etc.

val port = System.getenv(“PORT”)?.toIntOrNull() ?: 80

onEach()

‘onEach’ 是一个很小的扩展,但却对集合和序列很有用,这样就可以对集合和队列采用链式调用执行一些操作。

to be continue