Kotlin:2.0.0 的新特性

一、概述

kotlin 2.0.0版本英文官方文档

The Kotlin 2.0.0 release is out and the new Kotlin K2 compiler is Stable! Additionally, here are some other highlights:

Kotlin 2.0.0发布了,新的Kotlin K2编译器已经稳定了。此外,以下是其他一些亮点:

二、局部变量和进一步的作用域

以前,如果一个变量在if条件中被计算为非空,则该变量将被智能强制转换。关于这个变量的信息将在if块的范围内进一步共享。

但是,如果在if条件之外声明变量,则在if条件中无法获得有关该变量的信息,因此无法对其进行智能强制转换。这种行为在when表达式和while循环中也可以看到。

从Kotlin 2.0.0开始,如果在if、when或while条件中使用一个变量之前声明它,那么编译器收集的关于该变量的任何信息都可以在相应的块中进行智能转换。

当您想要将布尔条件提取到变量中时,这可能很有用。然后,您可以为变量指定一个有意义的名称,这将提高代码的可读性,并使以后在代码中重用该变量成为可能。例如:

三、使用逻辑或运算符进行类型检查

在Kotlin 2.0.0中,如果您将对象的类型检查与or操作符(||)结合起来,则会对其最接近的公共超类型进行智能强制转换。在此更改之前,总是对Any类型进行智能强制转换。

在这种情况下,您仍然必须手动检查对象类型,然后才能访问其任何属性或调用其函数。例如:

复制代码
class Cat {
    //咕噜声
    fun purr() {
        print("cat Purr purr")
    }
}

fun petAnimal(animal: Any) {
    val isCat = animal is Cat
    if (isCat) {
        //在Kotlin 2.0.0中,编译器可以访问关于isCat的信息,
        // 所以它知道动物被智能铸造为Cat类型。因此,可以调用purr()函数。
        
        //在Kotlin 1.9.20中,编译器不知道
        //调用purr()函数触发错误。
        // animal.purr()

        // 2.0之前的版本,
        // (animal as Cat).purr()
    }
}

fun main() {
    val ketty = Cat()
    petAnimal(ketty)
}

四、使用逻辑或运算符进行类型检查

在Kotlin 2.0.0中,如果您将对象的类型检查与or操作符(||)结合起来,则会对其最接近的公共超类型进行智能强制转换。在此更改之前,总是对Any类型进行智能强制转换。

在这种情况下,您仍然必须手动检查对象类型,然后才能访问其任何属性或调用其函数。例如:

复制代码
interface Status {
    fun signal() {}
}
interface Ok : Status
interface Postponed : Status
interface Declined : Status

fun signalCheck(signalStatus: Any) {
    if (signalStatus is Postponed || signalStatus is Declined) {
        // signalStatus is smart-cast to a common supertype Status;  signalStatus被智能转换为一个普通的超类型Status
//        signalStatus.signal()
        // Prior to Kotlin 2.0.0, signalStatus is smart cast; 在Kotlin 2.0.0之前,signalStatus是智能强制转换
        // to type Any, so calling the signal() function triggered an ; 输入Any,所以调用signal()函数会触发一个
        // Unresolved reference error. The signal() function can only; 未解析的引用错误。signal()函数只能
        // be called successfully after another type check: ; 在另一次类型检查后被成功调用:

        // check(signalStatus is Status)
        // signalStatus.signal()
    }
}

五、内联函数

在Kotlin 2.0.0中,K2编译器以不同的方式处理内联函数,允许它结合其他编译器分析来确定智能强制转换是否安全。

具体来说,内联函数现在被视为具有隐式的callsInPlace契约。这意味着任何传递给内联函数的lambda函数都会被就地调用。由于lambda函数是就地调用的,编译器知道lambda函数不会泄漏对其函数体中包含的任何变量的引用。

编译器使用这些知识和其他编译器分析来决定智能转换捕获的任何变量是否安全。例如:

复制代码
interface Processor {
    fun process()
}

inline fun inlineAction(f: () -> Unit) = f()

fun nextProcessor(): Processor? = null

fun runProcessor(): Processor? {
    var processor: Processor? = null
    inlineAction {
        // In Kotlin 2.0.0, the compiler knows that processor
        // is a local variable, and inlineAction() is an inline function, so
        // references to processor can't be leaked. Therefore, it's safe
        // to smart-cast processor.

        // If processor isn't null, processor is smart-cast
        if (processor != null) {
            // The compiler knows that processor isn't null, so no safe call
            // is needed
            processor.process()

            // In Kotlin 1.9.20, you have to perform a safe call:
            // processor?.process()
        }

        processor = nextProcessor()
    }

    return processor
}

六、函数类型的属性

在Kotlin的早期版本中,有一个bug意味着带有函数类型的类属性不能智能强制转换。我们在Kotlin 2.0.0和K2编译器中修复了这个行为。例如:

复制代码
class Holder(val provider: (() -> Unit)?) {
    fun process() {
        // In Kotlin 2.0.0, if provider isn't null, then
        // provider is smart-cast
        if (provider != null) {
            // The compiler knows that provider isn't null
            provider()

            // In 1.9.20, the compiler doesn't know that provider isn't
            // null, so it triggers an error:
            // Reference has a nullable type '(() -> Unit)?', use explicit '?.invoke()' to make a function-like call instead
        }
    }
}

如果重载调用操作符,也适用此更改。例如:

复制代码
interface Provider {
    operator fun invoke()
}

interface Processor : () -> String

class Holder(val provider: Provider?, val processor: Processor?) {
    fun process() {
        if (provider != null) {
            provider()
            // In 1.9.20, the compiler triggers an error:
            // Reference has a nullable type 'Provider?' use explicit '?.invoke()' to make a function-like call instead
        }
    }
}

七、异常处理

在Kotlin 2.0.0中,我们对异常处理进行了改进,以便将智能强制转换信息传递给catch和finally块。此更改使代码更安全,因为编译器会跟踪对象是否具有可空类型。例如:

复制代码
fun testString() {
    var stringInput: String? = null
    // stringInput is smart-cast to String type
    stringInput = ""
    try {
        // The compiler knows that stringInput isn't null
        println(stringInput.length)
        // 0

        // The compiler rejects previous smart cast information for
        // stringInput. Now stringInput has the String? type.
        stringInput = null

        // Trigger an exception
        if (2 > 1) throw Exception()
        stringInput = ""
    } catch (exception: Exception) {
        // In Kotlin 2.0.0, the compiler knows stringInput
        // can be null, so stringInput stays nullable.
        println(stringInput?.length)
        // null
        println("捕获到的异常信息:$exception")
        // In Kotlin 1.9.20, the compiler says that a safe call isn't
        // needed, but this is incorrect.
    }finally {
        println("执行了 finally 逻辑")
    }
}

运行结果

八、kt_200.kt文件代码

复制代码
package com.example.test.ktversion

// https://kotlinlang.org/docs/whatsnew20.html
class Cat {
    //咕噜声
    fun purr() {
        print("cat Purr purr")
    }
}

fun petAnimal(animal: Any) {
    val isCat = animal is Cat
    if (isCat) {
        //在Kotlin 2.0.0中,编译器可以访问关于isCat的信息,
        // 所以它知道动物被智能铸造为Cat类型。因此,可以调用purr()函数。

        //在Kotlin 1.9.20中,编译器不知道
        //调用purr()函数触发错误。
        // animal.purr()

        // 2.0之前的版本,
        // (animal as Cat).purr()
    }
}

// Type checks with logical or operator  将对象的类型检查与or操作符(||)结合起来
interface Status {
    fun signal() {}
}
interface Ok : Status
interface Postponed : Status
interface Declined : Status

fun signalCheck(signalStatus: Any) {
    if (signalStatus is Postponed || signalStatus is Declined) {
        // signalStatus is smart-cast to a common supertype Status;  signalStatus被智能转换为一个普通的超类型Status
//        signalStatus.signal()
        // Prior to Kotlin 2.0.0, signalStatus is smart cast; 在Kotlin 2.0.0之前,signalStatus是智能强制转换
        // to type Any, so calling the signal() function triggered an ; 输入Any,所以调用signal()函数会触发一个
        // Unresolved reference error. The signal() function can only; 未解析的引用错误。signal()函数只能
        // be called successfully after another type check: ; 在另一次类型检查后被成功调用:

        // check(signalStatus is Status)
        // signalStatus.signal()
    }
}

// Inline functions
interface Processor {
    fun process()
}

inline fun inlineAction(f: () -> Unit) = f()

fun nextProcessor(): Processor? = null

fun runProcessor(): Processor? {
    var processor: Processor? = null
    inlineAction {
        // In Kotlin 2.0.0, the compiler knows that processor ;  在Kotlin 2.0.0中,编译器知道这个处理器
        // is a local variable, and inlineAction() is an inline function, so ; 是一个局部变量,而inlineAction()是一个内联函数,所以
        // references to processor can't be leaked. Therefore, it's safe ; 对处理器的引用不能泄露。因此,它是安全的
        // to smart-cast processor. ; 到智能转换处理器。

        // If processor isn't null, processor is smart-cast; 如果processor不为空,则processor是smart-cast
        if (processor != null) {

            // The compiler knows that processor isn't null, so no safe call; 编译器知道处理器不是null,所以没有安全调用
            // is needed ; 需要
//            processor.process()

            // In Kotlin 1.9.20, you have to perform a safe call:
            // processor?.process()
        }
        processor = nextProcessor()
    }

    return processor
}

// Properties with function types  函数类型的属性
class Holder(val provider: (() -> Unit)?) {
    fun process() {
        // In Kotlin 2.0.0, if provider isn't null, then
        // provider is smart-cast
        if (provider != null) {
            // The compiler knows that provider isn't null
//            provider()

            // In 1.9.20, the compiler doesn't know that provider isn't
            // null, so it triggers an error:
            // Reference has a nullable type '(() -> Unit)?', use explicit '?.invoke()' to make a function-like call instead
        }
    }
}

//This change also applies if you overload your invoke operator. For example:
interface Provider {
    operator fun invoke()
}
interface Processor2 : () -> String

class Holder2(val provider: Provider?, val processor: Processor2?) {
    fun process() {
        if (provider != null) {
//            provider()
            // In 1.9.20, the compiler triggers an error:
            // Reference has a nullable type 'Provider?' use explicit '?.invoke()' to make a function-like call instead
        }
    }
}

// 异常处理
fun testString() {
    var stringInput: String? = null
    // stringInput is smart-cast to String type
    stringInput = ""
    try {
        // The compiler knows that stringInput isn't null
        println(stringInput.length)
        // 0

        // The compiler rejects previous smart cast information for
        // stringInput. Now stringInput has the String? type.
        stringInput = null

        // Trigger an exception
        if (2 > 1) throw Exception()
        stringInput = ""
    } catch (exception: Exception) {
        // In Kotlin 2.0.0, the compiler knows stringInput
        // can be null, so stringInput stays nullable.
        println(stringInput?.length)
        // null
        println("捕获到的异常信息:$exception")
        // In Kotlin 1.9.20, the compiler says that a safe call isn't
        // needed, but this is incorrect.
    }finally {
        println("执行了 finally 逻辑")
    }
}
fun main() {
    val ketty = Cat()
    petAnimal(ketty)
    testString()
}
相关推荐
Scott9999HH1 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
码智社2 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海2 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖3 小时前
JDK 26 新特性详解
java·开发语言
马优晨4 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区6 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大6 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai7 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
এ慕ོ冬℘゜7 小时前
前端基础:什么是时间戳?JS获取时间戳三种方法与实战用途
开发语言·前端·javascript
执明wa7 小时前
LayoutInflater详解: XML是如何变成View的?
android·xml·开发语言·android studio