終於,Kotlin 1.4的第一個預覽版發佈了,在新版本1.4-M1中,Kotlin又添加了一些新的功能,同時,也有一些重大的改進。本篇文章就帶你們一塊兒看看新版Kotlin中有哪些咱們指望添加和改進的功能。 web
若是使用在線編程,瀏覽器打開play.kotlinlang.org/,而後能夠選擇Kotlin版本爲1.4-M1 算法
若是使用的是Android Studio 或者IntelliJ IDE,你能夠直接升級插件到最新版本1.4-M1,步驟以下:編程
Early Access Preview X
,選擇對應版本
在Kotlin1.4中,使用了一個新的功能更增強大的類型推薦算法,或許你在Kotlin1.3中已經嘗試過這個算法了,在Kotlin1.3中,經過指定編譯器選項能夠實現。可是如今默認就使用它了。關於新的算法一些詳細的信息,能夠查看:youtrack.jetbrains.com/issues/KT?q… 下面只介紹一些重要的改進。後端
終於等到你,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做爲參數傳遞。
新的推理算法在許多狀況下會推斷類型
,在這些狀況下,舊的推理須要顯示指定它們的類型。例如,在下面的示例中,會將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 複製代碼
在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之類的強制轉換
)以使這種狀況起做用,如今這些強制轉換已再也不須要了。
請看下面的示例代碼:
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
變量智能地強制轉換爲特定類型的Cat
和Dog
以後,可使用不一樣的成員引用animal :: meow
和animal :: woof
。在檢查類型以後,就能夠訪問與子類型相對應的成員引用了。
好比下面這個列子:
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參數
或不採用任何參數
。所以就不會報上面的類型錯誤了。
先來看一段代碼:
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中,編譯器會正確推斷old
和new
參數類型爲String?
。
在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上。
mod
操做符另外一個不建議使用的函數是數字類型的mod
運算符,該運算符可計算除法運算後的餘數。在Kotlin 1.1中,它被rem()
函數取代。如今,將其從標準庫中徹底刪除。
標準庫中包含了一些將浮點類型的轉換爲整數類型的方法,如:toInt()
, toShort()
, toByte()
。可是因爲數值範圍狹小且變量大小較小,將浮點數轉換爲Short和Byte可能會致使意外結果。爲了解決這個問題,在1.4-M1中,咱們廢棄了Double
和Float
中的toShort()
和toByte()
方法。若是你仍然想吧浮點類型轉化爲Short或者Byte,該怎麼辦呢?那也好辦,進行兩步轉換,先將浮點類型轉爲Int,而後再將Int轉爲目標類型就能夠了。
咱們修改了通用反射API。如今,它包含全部三個目標平臺(JVM,JS,Native)上可用的成員,所以如今能夠確保相同的代碼可在其中任何一個平臺上上工做了。
從1.4-M1開始,咱們在kotlin-reflect.jar
中嵌入了Kotlin Reflection的Proguard / R8
配置, 有了這個更改,大多數使用了R8或者Proguard的Android項目在不用其餘任何配置的狀況下使用kotlin-reflect。你再也不須要複製粘貼Kotlin反射的Proguard規則。可是請注意,你仍然須要明確列出全部要考慮反射的API。
從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…
除了上面的一些改動以外,對於Kotlin/Js和Kotlin/iOS 平臺也有一些優化和改進,大體列出來看一下:
在kotlin.js
和multiplatform
Gradle插件中,引入了新的重要設置。在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項目的依賴項。
Kotlin 1.4-M1是第一個包含針對Kotlin / JS目標的新IR編譯器後端的版本。此後端是極大改進的基礎,也是Kotlin / JS與JavaScript和TypeScript交互方式發生某些變化的決定性因素。如下突出顯示的幾個功能均針對新的IR編譯器後端。雖然默認狀況下還沒有啓用它,咱們鼓勵你在項目中嘗試如下它。
如何使用新的後端?
在gradle.properties
配置文件中添加如下配置:
kotlin.js.compiler=ir // or both
複製代碼
若是須要爲IR編譯器後端和默認後端生成庫,則能夠選擇將此標誌設置爲both
。
關於both
的做用請看下面的章節介紹。
新的IR編譯器後端與原來默認的後端相比主要的變換是沒有二進制兼容,Kotlin / JS的兩個後端之間缺少這種兼容性,這意味着使用新的IR編譯器後端建立的庫沒法從默認後端使用,反之亦然。
與默認後端相比,新的IR編譯器後端進行了不少優化。生成的代碼與靜態分析器配合使用效果更好了,甚至能夠經過Google的Closure Compiler重新的IR編譯器後端運行生成的代碼,並使用其高級優化模式。
如今,標記爲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!
複製代碼
新的編譯器支持從Kotlin代碼生成TypeScript定義,對於配置produceExecutable()
配置項,而且使用了上面的@JsExport
的頂級聲明,將生成帶有TypeScript定義的.d.ts
文件。如上面的代碼,生成的文件以下所示:
// [...]
namespace blogpost {
class KotlinGreeter {
constructor(who: string)
greet(): string
}
function farewell(who: string): string
}
// [...]
複製代碼
Kotlin的早期版本爲Objective-C互操做中的泛型提供了實驗性支持。要從Kotlin代碼生成具備泛型的框架頭,必須使用-Xobjc-generics
選項。在1.4-M1中,默認就支持範型了。但在某些狀況下,這可能會破壞現有的調用Kotlin框架的Objective-C或Swift代碼。若是不想使用範型,請添加-Xno-objc-generics
選項
binaries.framework {
freeCompilerArgs += "-Xno-objc-generics"
}
複製代碼
在1.4中,咱們略微更改了從Kotlin生成Swift API異常處理的方式。Kotlin和Swift的錯誤處理存在根本的不一樣,全部Kotlin異常均未經檢查,而Swift僅檢查錯誤。所以,爲了使Swift代碼感知異常,需使用@Throws
註解標記Kotlin函數,該註解指定潛在異常類的列表。
當編譯爲Swift或Objective-C框架時,具備或正在繼承@Throws
註解的函數在Objective-C中表示爲NSError *
處理方法,而在Swift中表示爲throws
方法。
咱們一直在努力提升Kotlin / Native編譯和執行的總體性能。1.4-M1中,咱們爲提供了新的對象分配器,在某些基準測試中,它的運行速度提升了兩倍。當前,新的分配器是實驗性的,默認狀況下不使用。您可使用-Xallocator = mimalloc
切換至該選項。
以上就是Kotlin1.4-M1的一些變化,其中最令我驚喜的一個功能是:終於支持Kotlin interface SAM 轉換了。其餘的一些功能你們均可以去試一下,更多更詳細的信息請去官網瞭解,期待早點出release版吧!