Kotlin 1.4.30的新特性預(yù)覽

更多文章可以訪問我的博客Aengus | Blog

Kotlin 1.4.30是Kotlin 1.4的最后一個(gè)版本,其中包含了Kotlin 1.5中的即將發(fā)布的特性,包括inline value classes的穩(wěn)定、JVMrecord類的實(shí)驗(yàn)性支持以及sealed interface的實(shí)驗(yàn)性支持,如果想要體驗(yàn)這些特性,需要特別聲明版本:

compileKotlin {
    kotlinOptions {
        languageVersion = "1.5"
        apiVersion = "1.5"
    }
}

inline value classes的穩(wěn)定

在Kotlin 1.3中inline class已經(jīng)是Alpha狀態(tài),而在1.4.30版本中變?yōu)榱薆eta狀態(tài)。在Kotlin 1.5中將確定inline classes的概念并為了更一般的特性,將其變?yōu)?code>value class,我們將在下面提到。

inline class允許有一個(gè)并且只有一個(gè)val屬性,編譯器會(huì)自動(dòng)將內(nèi)聯(lián)類替換為其屬性,并且將使用內(nèi)聯(lián)類的函數(shù)的名稱進(jìn)行修改,如下:

inline class Color(val rgb: Int)

fun changeBackground(color: Color)
changeBackground(Color(255))

// 編譯后
fun changeBackground-euwHqFQ(color: Int) 
changeBackground-euwHqFQ(255) 

修改函數(shù)名稱的原因是防止由于JVM中類似方法的重載導(dǎo)致方法沖突。若在Java中使用Kotlin中定義的內(nèi)聯(lián)類,只能調(diào)用其空的構(gòu)造函數(shù),無(wú)法對(duì)內(nèi)聯(lián)類中包裹的屬性進(jìn)行賦值,但是可以定義接收內(nèi)聯(lián)類為參數(shù)的方法:

// Kotlin
inline class Color(val rgb: Int)

// Java
Color a = new Color();
a.getRgb() // OK
a.setRgb(1) // Error

類消除只有在將內(nèi)聯(lián)類傳給普通方法的時(shí)候才會(huì)發(fā)生,當(dāng)傳給泛型方法或者將內(nèi)聯(lián)類存儲(chǔ)在Collection中時(shí)并不會(huì)立刻進(jìn)行類消除,這有些類似Java中的裝箱,只有在進(jìn)行使用時(shí)才會(huì)進(jìn)行拆箱,這些都是自動(dòng)的。

對(duì)于Java調(diào)用修改JVM name

從1.4.30開始,可以給調(diào)用內(nèi)聯(lián)類的方法修改其Java調(diào)用時(shí)的名字,默認(rèn)由編譯器進(jìn)行修改以防止Java重載沖突,用法如下:

// Kotlin declarations
inline class Timeout(val millis: Long)

val Int.millis get() = Timeout(this.toLong())
val Int.seconds get() = Timeout(this * 1000L)

@JvmName("greetAfterTimeoutMillis")
fun greetAfterTimeout(timeout: Timeout)

// Kotlin usage
greetAfterTimeout(2.seconds)

// Java usage
greetAfterTimeoutMillis(2000);

@JvmName()不會(huì)對(duì)Kotlin生效,因?yàn)镵otlin傳入的類型是內(nèi)聯(lián)類。

初始化代碼塊

從1.4.30開始可以對(duì)內(nèi)聯(lián)類添加init代碼塊了:

inline class Name(val s: String) {
    init {
        require(s.isNotEmpty())
    }
}

注意:內(nèi)聯(lián)類的init代碼塊只有調(diào)用構(gòu)造方法的時(shí)候才會(huì)調(diào)用

Inline value classes

Kotlin 1.5為內(nèi)聯(lián)類帶來更具體的概念并且引入了更多的特性,其語(yǔ)法也變?yōu)榱?code>value class。

對(duì)于JVM來說,內(nèi)聯(lián)類是對(duì)只有一個(gè)參數(shù)的類的特別優(yōu)化。value class代表了更一般的概念并且會(huì)帶來更多的優(yōu)化:當(dāng)前的內(nèi)聯(lián)類、Valhalla項(xiàng)目原始類。

由于內(nèi)聯(lián)類是value class的一種優(yōu)化,所以必須要用和以往不同的方式聲明:

@JvmInline
value class Color(val rgb: Int)

原來的語(yǔ)法inline class還可以繼續(xù)使用一段時(shí)間,但是在1.5中使用會(huì)得到一個(gè)警告并且將來會(huì)被標(biāo)記為錯(cuò)誤。

Value classes

Value class代表了不可變的數(shù)據(jù)實(shí)體,現(xiàn)在(Kotlin 1.5)為了支持inline class,value class同樣也只允許一個(gè)參數(shù),但在之后的版本中將可以接收多個(gè)只讀(val)參數(shù):

value class Point(val x: Int, val y: Int)

Value class完全用來存儲(chǔ)數(shù)據(jù),沒有”標(biāo)識(shí)符“:===操作符不可以被調(diào)用,==操作符會(huì)比較其中所有的屬性;在Valhalla項(xiàng)目引入到JVM后,沒有”標(biāo)識(shí)符“這一特性將允許value class通過JVM原始類型來實(shí)現(xiàn)。

上面的特性也是value class不同于data class的一些點(diǎn)。

對(duì)JVM record類的支持

Java 14中引入了record class,其目的和Kotlin中的data class類似,都是作為數(shù)據(jù)的簡(jiǎn)單存儲(chǔ)。

Java record并不遵循JavaBean的規(guī)范,在JavaBean中的Getter方法為getX()getY(),而record class中則變?yōu)榱?code>x()和y()。現(xiàn)在Kotlin 1.4.30中也支持了這種語(yǔ)法,在Kotlin中調(diào)用record class和JavaBean類似:

// Java
record Point(int x, int y) { }
// Kotlin
fun foo(point: Point) {
    point.x // 屬性調(diào)用
    point.x() // 也可以
}

同樣也可以通過@JvmRecord注解將Kotlin中的data class轉(zhuǎn)為record class來給Java調(diào)用,這樣生成的Getter方法就變成x()而不是getX()。

@JvmRecord
data class Point(val x: Int, val y: Int)

需要注意的是@JvmRecord注解只有用JVM 15+的版本去編譯Kotlin代碼時(shí)才能夠使用。

密封接口及密封類提升

當(dāng)聲明一個(gè)類為sealed時(shí),將會(huì)限制其子類的繼承結(jié)構(gòu),這將允許when表達(dá)式的分支檢查。在1.4中,密封類有兩個(gè)限制:頂層類不能是密封接口;繼承密封類的所有的直接子類都必須在同一個(gè)文件中。

Kotlin 1.5移除了這兩個(gè)限制:可以將接口聲明為sealed,子類(包括密封類和密封接口)可以不在同一個(gè)文件中(但是需要和父類在相同的包下或者編譯單元中)。

sealed interface Expr
data class Const(val number: Double) : Expr
data class Sum(val e1: Expr, val e2: Expr) : Expr
object NotANumber : Expr

fun eval(expr: Expr): Double = when(expr) {
    is Const -> expr.number
    is Sum -> eval(expr.e1) + eval(expr.e2)
    NotANumber -> Double.NaN
}

密封接口同樣可以限制子類的繼承結(jié)構(gòu),除此之外,另一個(gè)用法是禁止外部庫(kù)實(shí)現(xiàn)或繼承接口。

在未來使用JVM支持

sealed classes預(yù)覽版支持已經(jīng)被引入到Java 15中,在將來編譯Kotlin sealed classes時(shí)會(huì)提供JVM的原生支持(可能是JVM 17或此特性穩(wěn)定后)。在Java中,顯式的列出密封類或接口的所有子類:

// Java
public sealed interface Expression
    permits Const, Sum, NotANumber { ... }

這些信息將使用新的PermittedSubclasses屬性存儲(chǔ)在類文件中,JVM會(huì)在運(yùn)行時(shí)識(shí)別sealed classes并且阻止未授權(quán)的子類的擴(kuò)展。

將來在使用最新的JVM編譯Kotlin時(shí),將啟動(dòng)新的sealed classes的JVM原生支持,編譯器會(huì)在字節(jié)碼中生成允許的子類列表來確保JVM支持以及格外的運(yùn)行時(shí)檢查:

// JVM 17+
Expr::class.java.permittedSubclasses // [Const, Sum, NotANunmber]

在Kotlin中并不用像Java一樣聲明所有繼承的子類,編譯器將會(huì)根據(jù)同包下的所有子類自動(dòng)生成。

理論上在舊的JVM版本中也可以定義Kotlin密封接口的Java子類,但是并沒有相關(guān)的限制,因?yàn)榕f的JVM并沒有相關(guān)功能。

原文地址

New Language Features Preview in Kotlin 1.4.30

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容