Kotlin1.4-M1發佈,終於支持Kotlin interface SAM轉換了!

終於,Kotlin 1.4的第一個預覽版發佈了,在新版本1.4-M1中,Kotlin又添加了一些新的功能,同時,也有一些重大的改進。本篇文章就帶你們一塊兒看看新版Kotlin中有哪些咱們指望添加和改進的功能。 web

1. 如何使用新版本?

若是使用在線編程,瀏覽器打開play.kotlinlang.org/,而後能夠選擇Kotlin版本爲1.4-M1 算法

若是使用的是Android Studio 或者IntelliJ IDE,你能夠直接升級插件到最新版本1.4-M1,步驟以下:編程

    1. 選擇Tools -> Kotlin ->Configure Kotlin Plugin Updates.
    1. 在更新列表中選擇 Early Access Preview X,選擇對應版本
    1. 點擊install 安裝重啓,就完成配置了。

2. 功能更強大的類型推薦算法

在Kotlin1.4中,使用了一個新的功能更增強大的類型推薦算法,或許你在Kotlin1.3中已經嘗試過這個算法了,在Kotlin1.3中,經過指定編譯器選項能夠實現。可是如今默認就使用它了。關於新的算法一些詳細的信息,能夠查看:youtrack.jetbrains.com/issues/KT?q… 下面只介紹一些重要的改進。後端

2.1. Kotlin方法和接口的SAM轉換

終於等到你,Kotlin1.4中能夠支持Kotlin interface SAM轉換了,這個真的過重要的了。瀏覽器

什麼是SAM轉換?可能有的同窗還不太瞭解,這裏先科普一下:app

SAM 轉換,即 Single Abstract Method Conversions,就是對於只有單個非默認抽象方法接口的轉換 —— 對於符合這個條件的接口(稱之爲 SAM Type ),在 Kotlin 中能夠直接用 Lambda 來表示 —— 固然前提是 Lambda 的所表示函數類型可以跟接口的中方法相匹配。框架

在Kotlin1.4以前,Kotlin是不支持Kotlin的SAM轉換的,能夠支持Java SAM轉換,官方給出的的解釋是:是 Kotlin 自己已經有了函數類型和高階函數,不須要在去SAM轉化。 這個解釋開發者並不買帳,若是你用過Java Lambda和Fuction Interface。當你切換到Kotlin時,就會很懵逼。看來Kotlin是意識到了這個,或者是看到開發者的反饋,終於支持了。jvm

Kotlin 的SAM轉換是什麼樣子呢?一塊兒看一個對比編輯器

1.4以前:ide

1.4以後:

// 注意需用fun 關鍵字聲明
fun interface Action {
    fun run()
}

fun runAction(a: Action) = a.run()

fun main() {
    // 傳遞一個對象,OK
    runAction(object : Action{
        override fun run() {
            println("run action")
        }
    })
   // 1.4-M1支持SAM,OK
    runAction {
        println("Hello, Kotlin 1.4!")
    }
}
複製代碼

能夠看到,在1.4以前,只能傳遞一個對象,是不支持Kotlin SAM的,而在1.4以後,能夠支持Kotlin SAM,可是用法有一丟丟變化。interface須要使用fun關鍵字聲明。使用fun關鍵字標記接口後,只要將此類接口做爲參數,就能夠將lambda做爲參數傳遞。

2.2. 更多場景的自動類型推斷

新的推理算法在許多狀況下會推斷類型,在這些狀況下,舊的推理須要顯示指定它們的類型。例如,在下面的示例中,會將lambda參數的類型正確推斷爲String?

val rulesMap: Map<String, (String?) -> Boolean> = mapOf(
    "weak" to { it != null },
    "medium" to { !it.isNullOrBlank() },
    "strong" to { it != null && "^[a-zA-Z0-9]+$".toRegex().matches(it) }
)

fun main() {
    println(rulesMap.getValue("weak")("abc!"))
    println(rulesMap.getValue("strong")("abc"))
    println(rulesMap.getValue("strong")("abc!"))
}
複製代碼

在1.3版本中,上面的代碼IDE是會報錯的,須要引入一個顯式的lambda參數,或將to替換爲具備顯式泛型參數的Pair構造函數以使其起做用。改成像下面這樣:

//須要顯示的lambda 參數
val rulesMap: Map<String, (String?) -> Boolean> = mapOf(
    "weak" to { it -> it != null },
    "medium" to { it -> !it.isNullOrBlank() },
    "strong" to { it ->  it != null && "^[a-zA-Z0-9]+$".toRegex().matches(it) }
)

fun main() {
    println(rulesMap.getValue("weak")("abc!"))
    println(rulesMap.getValue("strong")("abc"))
    println(rulesMap.getValue("strong")("abc!"))
}
複製代碼

打印結果以下:

true true false 複製代碼Process finished with exit code 0 複製代碼

2.3. Lambda內最後一個表達式的智能類型轉換

在Kotlin 1.3中,除非指定類型,不然lambda內的最後一個表達式不能智能強制轉換。所以,在如下示例中,Kotlin 1.3推斷String?做爲結果變量的類型:

val result = run {
    var str = currentValue()
    if (str == null) {
        str = "test"
    }
    str // Kotlin編譯器知道str在這裏不爲null
}
// result的類型在kotlin1.3中推斷爲String?,在Kotlin1.4中爲String
複製代碼

但在Kotlin 1.4中,因爲使用了新的推理算法,lambda內部的最後一個表達式獲得了智能轉換,而且此新的更精確的類型用於推斷所得的lambda類型。所以,結果變量的類型變爲String。而在Kotlin 1.3中,一般須要添加顯式強制轉換(!!或鍵入諸如String之類的強制轉換)以使這種狀況起做用,如今這些強制轉換已再也不須要了。

2.4. 可調用類型(Callable)引用的智能轉換

請看下面的示例代碼:

sealed class Animal
class Cat : Animal() {
    fun meow() {
        println("meow")
    }
}

class Dog : Animal() {
    fun woof() {
        println("woof")
    }
}

fun perform(animal: Animal) {
    val kFunction: KFunction<*> = when (animal) {
        is Cat -> animal::meow
        is Dog -> animal::woof
    }
    kFunction.call()
}

fun main() {
    perform(Cat())
}
複製代碼

在kotlin 1.3中,你沒法訪問智能轉換類型引用的成員,可是如今能夠了。 在將 Animal變量智能地強制轉換爲特定類型的CatDog以後,可使用不一樣的成員引用animal :: meowanimal :: woof。在檢查類型以後,就能夠訪問與子類型相對應的成員引用了。

2.5. 可調用(Callable)引用優化

好比下面這個列子:

fun foo(i: Int = 0): String = "$i!"

fun apply1(func: () -> String): String = func()
fun apply2(func: (Int) -> String): String = func(42)

fun main() {
    println(apply1(::foo))
    println(apply2(::foo))
}
複製代碼

在Kotlin 1.3中,foo函數解釋爲一個帶Int參數的函數,所以,apply1 會報類型錯誤。

而如今,使用具備默認參數值的函數的可調用引用獲得優化,foo函數的可調用引用能夠解釋爲採用一個Int參數不採用任何參數。所以就不會報上面的類型錯誤了。

2.6.委託屬性優化

先來看一段代碼:

fun main() {
    var prop: String? by Delegates.observable(null) { p, old, new ->
        println("$old → $new")
    }
    prop = "abc"
    prop = "xyz"
}
複製代碼

以上代碼在Kotlin 1.3 上編譯不過,由於在分析by後面的委託表達式時,不會考慮委託屬性的類型,所以會報類型錯誤。可是如今的kotlin 1.4-M1中,編譯器會正確推斷oldnew參數類型爲String?

3.標準庫更改

3.1. 廢棄試驗性的協程API

在1.3.0版中,咱們不推薦使用kotlin.coroutines.experimental API,而推薦使用kotlin.coroutines。在1.4-M1中,咱們將從標準庫中刪除kotlin.coroutines.experimental完成棄用。對於那些仍然在JVM上使用它的,咱們提供了一個兼容庫: kotlin-coroutines-experimental-compat.jar來替換它。咱們將其與Kotlin 1.4-M1一塊兒發佈到了Bintray上。

3.2. 刪除已廢棄的mod操做符

另外一個不建議使用的函數是數字類型的mod運算符,該運算符可計算除法運算後的餘數。在Kotlin 1.1中,它被rem()函數取代。如今,將其從標準庫中徹底刪除。

3.3. 廢棄從浮點類型到Byte和Short的轉換

標準庫中包含了一些將浮點類型的轉換爲整數類型的方法,如:toInt(), toShort(), toByte()。可是因爲數值範圍狹小且變量大小較小,將浮點數轉換爲Short和Byte可能會致使意外結果。爲了解決這個問題,在1.4-M1中,咱們廢棄了DoubleFloat中的toShort()toByte()方法。若是你仍然想吧浮點類型轉化爲Short或者Byte,該怎麼辦呢?那也好辦,進行兩步轉換,先將浮點類型轉爲Int,而後再將Int轉爲目標類型就能夠了。

3.4. 通用的反射API

咱們修改了通用反射API。如今,它包含全部三個目標平臺(JVM,JS,Native)上可用的成員,所以如今能夠確保相同的代碼可在其中任何一個平臺上上工做了。

3.5. 用於Kotlin反射的Proguard配置

從1.4-M1開始,咱們在kotlin-reflect.jar中嵌入了Kotlin Reflection的Proguard / R8配置, 有了這個更改,大多數使用了R8或者Proguard的Android項目在不用其餘任何配置的狀況下使用kotlin-reflect。你再也不須要複製粘貼Kotlin反射的Proguard規則。可是請注意,你仍然須要明確列出全部要考慮反射的API。

4. Kotlin/JVM

從1.3.70版開始,Kotlin可以在JVM字節碼(目標版本1.8+)中生成類型註解,以便它們在運行時可用。社區要求此功能已有一段時間,由於它使使用某些現有Java庫變得更加容易,併爲新庫的做者提供了更多的擴展能力。

在如下示例中,能夠在字節碼中發出String類型的@Foo批註,而後由庫代碼使用:

@Target(AnnotationTarget.TYPE)
annotation class Foo

class A {
    fun foo()@Foo String = "OK"
}
複製代碼

關於具體如何使用,能夠看一下這篇博客:blog.jetbrains.com/kotlin/2020…

5. 其餘一些改動

除了上面的一些改動以外,對於Kotlin/Js和Kotlin/iOS 平臺也有一些優化和改進,大體列出來看一下:

5.1 Kotlin/JS
5.1.1. Gradle DSL 更改

kotlin.jsmultiplatformGradle插件中,引入了新的重要設置。在build.gradle.kts文件的目標塊內,若是您想在構建過程當中生成.js工件,則能夠配置並使用produceExecutable()

kotlin {
    target {
        useCommonJs()

        produceExecutable()
        
        browser {}
    }
}
複製代碼
  • 若是您正在編寫Kotlin / JS庫,則能夠省略ProduceExecutable()配置。

  • 當使用新的IR編譯器後端(有關此內容的更多詳細信息,在下文中)時,省略此設置意味着將不會生成可執行的JS文件(所以,構建過程將運行得更快)。將在build / libs文件夾中生成一個klib文件,該文件可從其餘Kotlin / JS項目使用,也可做爲同一項目中的依賴項。若是您未明確指定produceExecutable(),則默認狀況下會發生這種狀況。

使用produceExecutable()將生成可從JavaScript生態系統執行的代碼,不管其具備本身的入口點仍是做爲JavaScript庫,這將生成實際的JavaScript文件,該文件能夠在節點解釋器中運行,能夠嵌入HTML頁面中並在瀏覽器中執行,或用做JavaScript項目的依賴項。

5.1.2. 新後端

Kotlin 1.4-M1是第一個包含針對Kotlin / JS目標的新IR編譯器後端的版本。此後端是極大改進的基礎,也是Kotlin / JS與JavaScript和TypeScript交互方式發生某些變化的決定性因素。如下突出顯示的幾個功能均針對新的IR編譯器後端。雖然默認狀況下還沒有啓用它,咱們鼓勵你在項目中嘗試如下它。

(1)如何使用新的後端?

gradle.properties配置文件中添加如下配置:

kotlin.js.compiler=ir // or both
複製代碼

若是須要爲IR編譯器後端和默認後端生成庫,則能夠選擇將此標誌設置爲both

關於both 的做用請看下面的章節介紹。

(2)無二進制兼容

新的IR編譯器後端與原來默認的後端相比主要的變換是沒有二進制兼容,Kotlin / JS的兩個後端之間缺少這種兼容性,這意味着使用新的IR編譯器後端建立的庫沒法從默認後端使用,反之亦然。

(3)DCE 優化

與默認後端相比,新的IR編譯器後端進行了不少優化。生成的代碼與靜態分析器配合使用效果更好了,甚至能夠經過Google的Closure Compiler重新的IR編譯器後端運行生成的代碼,並使用其高級優化模式。

(4)支持聲明導出到JavaScript

如今,標記爲public的聲明再也不自動導出,要使頂級聲明能在JavaScript或TypeScript中使用,請使用@JsExport註解。

package blogpost

@JsExport
class KotlinGreeter(private val who: String) {
    fun greet() = "Hello, $who!"
}

@JsExport
fun farewell(who: String) = "Bye, $who!"

fun secretGreeting(who: String) = "Sup, $who!" // only from Kotlin!
複製代碼
(5)支持TypeScript定義

新的編譯器支持從Kotlin代碼生成TypeScript定義,對於配置produceExecutable()配置項,而且使用了上面的@JsExport的頂級聲明,將生成帶有TypeScript定義的.d.ts文件。如上面的代碼,生成的文件以下所示:

// [...]
namespace blogpost {
    class KotlinGreeter {
        constructor(who: string)
        greet(): string
    }
    function farewell(who: string): string
}
// [...]
複製代碼
6. Kotlin/Native的一些變動
6.1. Objective-C默認支持泛型

Kotlin的早期版本爲Objective-C互操做中的泛型提供了實驗性支持。要從Kotlin代碼生成具備泛型的框架頭,必須使用-Xobjc-generics選項。在1.4-M1中,默認就支持範型了。但在某些狀況下,這可能會破壞現有的調用Kotlin框架的Objective-C或Swift代碼。若是不想使用範型,請添加-Xno-objc-generics選項

binaries.framework {
     freeCompilerArgs += "-Xno-objc-generics"
}
複製代碼
6.2. Objective-C/Swift 互操做中異常處理變化

在1.4中,咱們略微更改了從Kotlin生成Swift API異常處理的方式。Kotlin和Swift的錯誤處理存在根本的不一樣,全部Kotlin異常均未經檢查,而Swift僅檢查錯誤。所以,爲了使Swift代碼感知異常,需使用@Throws註解標記Kotlin函數,該註解指定潛在異常類的列表。

當編譯爲Swift或Objective-C框架時,具備或正在繼承@Throws註解的函數在Objective-C中表示爲NSError *處理方法,而在Swift中表示爲throws方法。

6.3. 性能提高

咱們一直在努力提升Kotlin / Native編譯和執行的總體性能。1.4-M1中,咱們爲提供了新的對象分配器,在某些基準測試中,它的運行速度提升了兩倍。當前,新的分配器是實驗性的,默認狀況下不使用。您可使用-Xallocator = mimalloc切換至該選項。

7.總結

以上就是Kotlin1.4-M1的一些變化,其中最令我驚喜的一個功能是:終於支持Kotlin interface SAM 轉換了。其餘的一些功能你們均可以去試一下,更多更詳細的信息請去官網瞭解,期待早點出release版吧!

相關文章
相關標籤/搜索