Kotlin 1.2 新特性

简介:

在Kotlin 1.1中,团队正式发布了JavaScript目标,允许开发者将Kotlin代码编译为JS并在浏览器中运行。在Kotlin 1.2中,团队增加了在JVM和JavaScript之间重用代码的可能性。现在,使用Kotlin编写的代码,可以在所有的应用程序中(包括后端,浏览器前端和Android移动应用程序)中重复使用。

这里写图片描述

想要体验Kotlin1.2新功能的同学,可以下载官方提供的IntelliJ IDEA 2017.3开发工具,或者升级老的IDE,当然也可以通过在线网站来体验。

跨平台

跨平台项目是 Kotlin 1.2 中的一个新的实验性功能,它允许开发者从相同的代码库构建应用程序的多个层——后端、前端和Android应用程序,在这个跨平台方案中,主要包含三个模块。

  • 通用(common)模块:包含非特定于任何平台的代码,以及不附带依赖于平台的 API 实现的声明。
  • 平台(platform)模块:包含用于特定平台的通用模块中与平台相关声明的实现,以及其他平台相关代码。
  • 常规(regular)模块:针对特定平台,可以是平台模块的某些依赖,也可以是依赖的平台模块。

这里写图片描述
要从通用模块中调用特定于平台的代码,可以指定所需的声明:所有特定于平台的模块需要提供实际实现声明。而在为特定平台编译多平台项目时,会生成通用及特定平台相关部分的代码。可以通过 expected 以及 actual 声明来表达通用代码对平台特定部分的依赖关系。expected 声明指定了一个 API(类、接口、注释、顶层声明等)。actual 声明或是 API 的平台相关实现,或是在外部库中 API 现有实现的别名引用。下面是官方提供的相关例子:

通用模块

// expected platform-specific API:
expect fun hello(world: String): String

fun greet() {
    // usage of the expected API:
    val greeting = hello("multi-platform world")
    println(greeting)
}

expect class URL(spec: String) {
    open fun getHost(): String
    open fun getPath(): String
}

JVM 平台代码

actual fun hello(world: String): String =
    "Hello, $world, on the JVM platform!"

// using existing platform-specific implementation:
actual typealias URL = java.net.URL

想要获取更多跨平台相关的信息,可以查看官方资料介绍。

请注意,目前跨平台项目只是一个实验性功能,这意味着该功能已经可以使用,但可能需要在后续版本中更改设计

编译性能

在1.2的开发过程中,团队花了很多精力来优化编译系统,据官方提供的资料显示,与Kotlin 1.1相比,Kotlin带来了大约25%的性能提升,并且看到了可以进一步改进的巨大潜力,这些改进将在1.2.x更新中发布。
下图显示了使用Kotlin构建两个大型JetBrains项目的编译时间差异。
这里写图片描述

语法与库优化

除了上面介绍的改动之外,Kotlin还在语法层面进行了部分改进,优化的部分有。

通过注解声明数组变量

自Kotlin1.2开始,系统允许通过注解声明数组参数,从而取代arrayOf函数的数组声明方式。例如:

@CacheConfig(cacheNames = ["books", "default"])
public class BookRepositoryImpl {
    // ...
}

可见,新的数组参数声明语法依赖于注解方式。

关键字lateinit

lateinit 和lazy一样,是 Kotlin中的两种不同的延迟初始化技术。在Kotlin1.2版本中,使用lateinit修饰符能够用于全局变量和局部变量了,也就是说,二者都允许延迟初始化。例如,当lambda表达式在构造一个对象时,允许将延迟初始化属性作为构造参数传过去。

class Node<T>(val value: T, val next: () -> Node<T>)

fun main(args: Array<String>) {
    // A cycle of three nodes:
    lateinit var third: Node<Int>

    val second = Node(2, next = { third })
    val first = Node(1, next = { second })

    third = Node(3, next = { first })

    
    val nodes = generateSequence(first) { it.next() }
    println("Values in the cycle: ${nodes.take(7).joinToString { it.value.toString() }}, ...")
}

运行上面的代码,输出结果如下:

Values in the cycle: 1, 2, 3, 1, 2, 3, 1, ...

延迟初始化属性检测

通过访问属性的isInitialized字段,现在开发者可以检查一个延迟初始化属性是否已经初始化。

class Foo {
    lateinit var lateinitVar: String
    
    fun initializationLogic() {
        println("isInitialized before assignment: " + this::lateinitVar.isInitialized)
        lateinitVar = "value"
        println("isInitialized after assignment: " + this::lateinitVar.isInitialized)    

    }
}

fun main(args: Array<String>) {
    Foo().initializationLogic()
}

运行结果为:

isInitialized before assignment: false
isInitialized after assignment: true

内联函数默认参数

自1.2版本开始,Kotlin允许允许给内联函数的函数参数填写默认参数了。

inline fun <E> Iterable<E>.strings(transform: (E) -> String = { it.toString() }) = 
map { transform(it) }

val defaultStrings = listOf(1, 2, 3).strings()
val customStrings = listOf(1, 2, 3).strings { "($it)" } 

fun main(args: Array<String>) {
    println("defaultStrings = $defaultStrings")
    println("customStrings = $customStrings")

运行结果为:

defaultStrings = [1, 2, 3]
customStrings = [(1), (2), (3)]

变量类型推断

大家都知道,Kotlin的类型推断系统是非常强大的,现在Kotlin编译器也支持通过强制转换的信息,来推断出变量类型了。比如说,如果你在调用一个返回“T”的泛型方法时,并将它的返回值“T”转换为特定类型如“Foo”,编译器就会推断出这个方法调用中的“T”其实是“Foo”类型。

这个对安卓开发者而言尤其重要,因为自从API26(Android7.0)开始,findViewById变成了泛型方法,然后编译器也会正确分析该方法的调用返回值。

val button = findViewById(R.id.button) as Button

智能转换

当一个变量为某个安全表达式(如校验非空)所赋值时,智能转换也同样运用于这个安全调用的接收者。

fun countFirst(s: Any): Int {
    val firstChar = (s as? CharSequence)?.firstOrNull()
    if (firstChar != null)
    return s.count { it == firstChar } // 输入参数s被智能转换为CharSequence类型

    val firstItem = (s as? Iterable<*>)?.firstOrNull()
    if (firstItem != null)
    return s.count { it == firstItem } // 输入参数s被智能转换为Iterable<*>类型  

    
    return -1
}

fun main(args: Array<String>) {
    val string = "abacaba"
    val countInString = countFirst(string)
    println("called on \"$string\": $countInString")
    
    val list = listOf(1, 2, 3, 1, 2)
    val countInList = countFirst(list)
    println("called on $list: $countInList")
}

运行结果为:

called on "abacaba": 4
called on [1, 2, 3, 1, 2]: 2

另外,Lamba表达式同样支持对局部变量进行智能转换,前提是该局部变量只在Lamba表达式之前修改过。

fun main(args: Array<String>) {
    val flag = args.size == 0
    
    var x: String? = null
    if (flag) x = "Yahoo!"

    run {
        if (x != null) {
            println(x.length) // x is smart cast to String
        }
    }

}

运行结果为:
6

foo的简写

为了简化调用成员的引用,现在可以不用this关键字,::foo而不用明确的接收者this::foo。这也使得可调用的引用在你引用外部接收者的成员的lambda中更方便。

弃用

Kotlin1.2版本也弃用了很多不合理的东西。

弃用:枚举条目中的嵌套类型

在枚举条目中,inner class由于初始化逻辑中的问题,定义一个非嵌套的类型已经被弃用了。这会在Kotlin 1.2中引起警告,并将在Kotlin 1.3中出错。

弃用:vararg单个命名参数

为了与注释中的数组文字保持一致,在命名形式(foo(items = i))中传递可变参数的单个项目已被弃用。请使用具有相应数组工厂功能的扩展运算符。

foo(items = *intArrayOf(1))

在这种情况下,有一种优化可以消除冗余阵列的创建,从而防止性能下降。单参数形式在Kotlin 1.2中产生警告,并将被放在Kotlin 1.3中。

弃用:扩展Throwable的泛型内部类

继承的泛型类型的内部类Throwable可能会违反类型安全性,因此已被弃用,Kotlin 1.2中有警告,Kotlin 1.3中有错误。

弃用:只读属性的后台字段

field = ...已经废弃了在自定义获取器中分配只读属性的后台字段,Kotlin 1.2中有警告,Kotlin 1.3中有错误。

标准库

Kotlin标准库与拆分包

Kotlin标准库现在完全兼容Java 9模块系统,该系统禁止拆分包(多个jar文件在同一个包中声明类)。为了支持这一点,新的文物kotlin-stdlib-jdk7 和kotlin-stdlib-jdk8介绍,取代旧的kotlin-stdlib-jre7和kotlin-stdlib-jre8。

为确保与新模块系统的兼容性,Kotlin做出的另一个更改是将kotlin.reflect从kotlin-reflect库中移除。如果您正在使用它们,则需要切换到使用kotlin.reflect.full软件包中的声明,这是自Kotlin 1.1以来支持的声明。

窗口,分块,zipWithNext

为新的扩展Iterable,Sequence以及CharSequence覆盖这些用例如缓冲或批处理(chunked),滑动窗口和计算滑动平均(windowed),和随后的项目的处理对(zipWithNext)。

fun main(args: Array<String>) {
    val items = (1..9).map { it * it }

    val chunkedIntoLists = items.chunked(4)
    val points3d = items.chunked(3) { (x, y, z) -> Triple(x, y, z) }
    val windowed = items.windowed(4)
    val slidingAverage = items.windowed(4) { it.average() }
    val pairwiseDifferences = items.zipWithNext { a, b -> b - a }

    
    println("items: $items\n")
    
    println("chunked into lists: $chunkedIntoLists")
    println("3D points: $points3d")
    println("windowed by 4: $windowed")
    println("sliding average by 4: $slidingAverage")
    println("pairwise differences: $pairwiseDifferences")
}

fill, replaceAll, shuffle/shuffled

为了操纵列表,Kotlin加入了一组扩展函数:fill,replaceAll和shuffle对MutableList,shuffled用于只读List。

fun main(args: Array<String>) {
    val items = (1..5).toMutableList()

    items.shuffle()
    println("Shuffled items: $items")

    items.replaceAll { it * 2 }
    println("Items doubled: $items")

    items.fill(5)
    println("Items filled with 5: $items")

}

运行结果为:
Shuffled items: [5, 3, 1, 2, 4]
Items doubled: [10, 6, 2, 4, 8]
Items filled with 5: [5, 5, 5, 5, 5]

数学运算

为了满足一些特殊的需求,Kotlin 1.2添加了一些常见的数学运算API。

  • 常量:PI和E;
  • 三角函数:cos,sin,tan和它们的反:acos,asin,atan,atan2,
  • 双曲:cosh,sinh,tanh和它们的反:acosh,asinh,atanh
  • 求幂:pow(扩展函数),sqrt,,hypot ;expexpm1
  • 对数:log,log2,log10,ln,ln1p,
  • 四舍五入: ceil,floor,truncate,round(半连)的功能;
    roundToInt,roundToLong(半整数)扩展函数;
  • 符号和绝对值: abs和sign功能; absoluteValue和sign扩展属性; withSign 扩展功能;max和min两个价值观;
  • 二进制表示: ulp 扩展属性; nextUp,nextDown,nextTowards扩展函数;toBits,toRawBits,Double.fromBits(这些是在kotlin包)。

正则表达式可序列化

现在,Kotlin可以使用Serializable来序列化正则表达式的层次结构。

JVM

构造函数调用规范化

自1.0版以来,Kotlin支持复杂控制流的表达式,例如try-catch表达式和内联函数调用。但是,如果构造函数调用的参数中存在这样的表达式时,一些字节码处理工具不能很好地处理这些代码。为了缓解这种字节码处理工具的用户的这个问题,我们添加了一个命令行选项(-Xnormalize-constructor-calls=MODE),它告诉编译器为这样的结构生成更多的类Java字节码。

其中,这里的MODE有以下情况:

  • disable (默认) - 以和Kotlin 1.0和1.1相同的方式生成字节码;
  • enable - 为构造函数调用生成类似Java的字节码。这可以改变类加载和初始化的顺序;
  • preserve-class-initialization -为构造函数调用生成类似Java的字节码,确保保持类的初始化顺序。这可能会影响应用程序的整体性能;只有在多个类之间共享一些复杂的状态并在类初始化时更新时才使用它。

Java默认方法调用

在Kotlin 1.2之前,接口成员在针对JVM 1.6的情况下重写Java默认方法会在超级调用上产生一个警告:Super calls to Java default methods are deprecated in JVM target 1.6. Recompile with '-jvm-target 1.8'。在Kotlin 1.2中,会出现一个错误,因此需要使用JVM target 1.8来编译这些代码。

x.equals(null)

调用x.equals(null)上被映射到Java原始(平台类型Int!,Boolean!,Short!, ,Long!,Float!,Double!)Char!返回不正确true时x为空。从Kotlin 1.2开始,调用x.equals(...)一个平台类型的null值会抛出一个NPE (但是x == ...不会)。

要返回到1.2之前的行为,请将该标志传递-Xno-exception-on-explicit-equals-for-boxed-null给编译器。

内联扩展空修复

在以前的版本中,在平台类型的空值上调用的内联扩展函数没有检查接收器是否为null,并因此允许null转义到其他代码中。Kotlin 1.2中强制执行此检查,如果接收方为空,则抛出异常。

JavaScript

TypedArrays支持

JS类型的数组支持将Kotlin原始数组(例如IntArray,DoubleArray)转换为JavaScript类型的数组,这以前是可选入功能,默认情况下已启用。

除此之外,Kotlin的编译器现在提供一个将所有警告视为错误的选项。使用-Werror命令行,或者修改如下配置:

compileKotlin {
    kotlinOptions.allWarningsAsErrors = true
}
目录
相关文章
|
3月前
|
安全 Java 编译器
Kotlin 1.6 正式发布,带来哪些新特性?
Kotlin 1.6 正式发布,带来哪些新特性?
29 0
|
6月前
|
IDE Java 开发工具
Kotlin 1.8.0 现已发布,有那些新特性?
Kotlin 1.8.0 现已发布,有那些新特性?
104 0
Kotlin 1.8.0 现已发布,有那些新特性?
|
IDE Java 编译器
Kotlin 1.5 新特性:密封接口比密封类强在哪?
Kotlin 1.5 推出了密封接口(Sealed Interface),这与密封类(Sealed Class)有什么区别呢?
343 0
Kotlin 1.5 新特性:密封接口比密封类强在哪?
|
JavaScript 前端开发 Java
Kotlin 1.2 新特性
在Kotlin 1.1中,团队正式发布了JavaScript目标,允许开发者将Kotlin代码编译为JS并在浏览器中运行。在Kotlin 1.2中,团队增加了在JVM和JavaScript之间重用代码的可能性。
7533 0
|
Kotlin
Kotlin新特性:区间
一、概念 一个数学上的概念、表示范围 ClosedRange 的子类,IntRange最常用 二、基本的写法 package net.
834 0
|
JavaScript 前端开发 Java
Kotlin 与 Java 8 的重要新特性以及 Java 9、10 的发展规划
Java 8可谓是自Java 5以来最具革命性的版本了,她在语言、编译器、类库、开发工具以及Java虚拟机等方面都带来了不少新特性。我们来一一回顾一下这些特性。
1319 0
|
15天前
|
移动开发 Java Android开发
构建高效Android应用:探究Kotlin与Java的性能差异
【4月更文挑战第3天】在移动开发领域,性能优化一直是开发者关注的焦点。随着Kotlin的兴起,其在Android开发中的地位逐渐上升,但关于其与Java在性能方面的对比,尚无明确共识。本文通过深入分析并结合实际测试数据,探讨了Kotlin与Java在Android平台上的性能表现,揭示了在不同场景下两者的差异及其对应用性能的潜在影响,为开发者在选择编程语言时提供参考依据。
|
17天前
|
数据库 Android开发 开发者
构建高效Android应用:Kotlin协程的实践指南
【4月更文挑战第2天】随着移动应用开发的不断进步,开发者们寻求更流畅、高效的用户体验。在Android平台上,Kotlin语言凭借其简洁性和功能性赢得了开发社区的广泛支持。特别是Kotlin协程,作为一种轻量级的并发处理方案,使得异步编程变得更加简单和直观。本文将深入探讨Kotlin协程的核心概念、使用场景以及如何将其应用于Android开发中,以提高应用性能和响应能力。通过实际案例分析,我们将展示协程如何简化复杂任务,优化资源管理,并为最终用户提供更加流畅的体验。
|
25天前
|
调度 数据库 Android开发
构建高效Android应用:Kotlin协程的实践与优化
在Android开发领域,Kotlin以其简洁的语法和平台友好性成为了开发的首选语言。其中,Kotlin协程作为处理异步任务的强大工具,它通过提供轻量级的线程管理机制,使得开发者能够在不阻塞主线程的情况下执行后台任务,从而提升应用性能和用户体验。本文将深入探讨Kotlin协程的核心概念,并通过实例演示如何在实际的Android应用中有效地使用协程进行网络请求、数据库操作以及UI的流畅更新。同时,我们还将讨论协程的调试技巧和常见问题的解决方法,以帮助开发者避免常见的陷阱,构建更加健壮和高效的Android应用。
33 4
|
28天前
|
移动开发 Java Android开发
构建高效Android应用:Kotlin协程的实践之路
【2月更文挑战第31天】 在移动开发领域,性能优化和流畅的用户体验一直是开发者追求的目标。随着Kotlin语言的流行,其异步编程解决方案——协程(Coroutines),为Android应用带来了革命性的并发处理能力。本文将深入探讨Kotlin协程的核心概念、设计原理以及在Android应用中的实际应用案例,旨在帮助开发者掌握这一强大的工具,从而提升应用的性能和响应能力。