終于,Kotlin 1.4的第一個預(yù)覽版發(fā)布了,在新版本1.4-M1中,Kotlin又添加了一些新的功能,同時,也有一些重大的改進。本篇文章就帶大家一起看看新版Kotlin中有哪些我們期望添加和改進的功能。

1. 如何使用新版本?
如果使用在線編程,瀏覽器打開https://play.kotlinlang.org/,然后可以選擇Kotlin版本為1.4-M1

如果使用的是Android Studio 或者IntelliJ IDE,你可以直接升級插件到最新版本1.4-M1,步驟如下:
- 選擇Tools -> Kotlin ->Configure Kotlin Plugin Updates.

- 在更新列表中選擇
Early Access Preview X,選擇對應(yīng)版本
- 在更新列表中選擇
- 點擊install 安裝重啟,就完成配置了。
2. 功能更強大的類型推薦算法
在Kotlin1.4中,使用了一個新的功能更加強大的類型推薦算法,或許你在Kotlin1.3中已經(jīng)嘗試過這個算法了,在Kotlin1.3中,通過指定編譯器選項可以實現(xiàn)。但是現(xiàn)在默認就使用它了。關(guān)于新的算法一些詳細的信息,可以查看:https://youtrack.jetbrains.com/issues/KT?q=Tag:%20fixed-in-new-inference%20&_ga=2.58428450.988595807.1586745008-1408654980.1539842787

下面只介紹一些重要的改進。
2.1. Kotlin方法和接口的SAM轉(zhuǎn)換
終于等到你,Kotlin1.4中可以支持Kotlin interface SAM轉(zhuǎn)換了,這個真的太重要的了。
什么是SAM轉(zhuǎn)換?可能有的同學還不太了解,這里先科普一下:
SAM 轉(zhuǎn)換,即 Single Abstract Method Conversions,就是對于只有單個非默認抽象方法接口的轉(zhuǎn)換 —— 對于符合這個條件的接口(稱之為 SAM Type ),在 Kotlin 中可以直接用 Lambda 來表示 —— 當然前提是 Lambda 的所表示函數(shù)類型能夠跟接口的中方法相匹配。
在Kotlin1.4之前,Kotlin是不支持Kotlin的SAM轉(zhuǎn)換的,可以支持Java SAM轉(zhuǎn)換,官方給出的的解釋是:是 Kotlin 本身已經(jīng)有了函數(shù)類型和高階函數(shù),不需要在去SAM轉(zhuǎn)化。 這個解釋開發(fā)者并不買賬,如果你用過Java Lambda和Fuction Interface。當你切換到Kotlin時,就會很懵逼。看來Kotlin是意識到了這個,或者是看到開發(fā)者的反饋,終于支持了。
Kotlin 的SAM轉(zhuǎn)換是什么樣子呢?一起看一個對比
1.4之前:

1.4之后:
// 注意需用fun 關(guān)鍵字聲明
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關(guān)鍵字聲明。使用fun關(guān)鍵字標記接口后,只要將此類接口作為參數(shù),就可以將lambda作為參數(shù)傳遞。
2.2. 更多場景的自動類型推斷
新的推理算法在許多情況下會推斷類型,在這些情況下,舊的推理需要顯示指定它們的類型。例如,在下面的示例中,會將lambda參數(shù)的類型正確推斷為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參數(shù),或?qū)?code>to替換為具有顯式泛型參數(shù)的Pair構(gòu)造函數(shù)以使其起作用。改為像下面這樣:
//需要顯示的lambda 參數(shù)
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!"))
}
打印結(jié)果如下:
true
true
false
Process finished with exit code 0
2.3. Lambda內(nèi)最后一個表達式的智能類型轉(zhuǎn)換
在Kotlin 1.3中,除非指定類型,否則lambda內(nèi)的最后一個表達式不能智能強制轉(zhuǎn)換。因此,在以下示例中,Kotlin 1.3推斷String?作為結(jié)果變量的類型:
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內(nèi)部的最后一個表達式得到了智能轉(zhuǎn)換,并且此新的更精確的類型用于推斷所得的lambda類型。因此,結(jié)果變量的類型變?yōu)镾tring。而在Kotlin 1.3中,通常需要添加顯式強制轉(zhuǎn)換(!!或鍵入諸如String之類的強制轉(zhuǎn)換)以使這種情況起作用,現(xiàn)在這些強制轉(zhuǎn)換已不再需要了。
2.4. 可調(diào)用類型(Callable)引用的智能轉(zhuǎn)換
請看下面的示例代碼:
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中,你無法訪問智能轉(zhuǎn)換類型引用的成員,但是現(xiàn)在可以了。
在將 Animal變量智能地強制轉(zhuǎn)換為特定類型的Cat和Dog之后,可以使用不同的成員引用animal :: meow和animal :: woof。在檢查類型之后,就可以訪問與子類型相對應(yīng)的成員引用了。
2.5. 可調(diào)用(Callable)引用優(yōu)化
比如下面這個列子:
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函數(shù)解釋為一個帶Int參數(shù)的函數(shù),因此,apply1 會報類型錯誤。

而現(xiàn)在,使用具有默認參數(shù)值的函數(shù)的可調(diào)用引用得到優(yōu)化,foo函數(shù)的可調(diào)用引用可以解釋為采用一個Int參數(shù)或不采用任何參數(shù)。因此就不會報上面的類型錯誤了。
2.6.委托屬性優(yōu)化
先來看一段代碼:
fun main() {
var prop: String? by Delegates.observable(null) { p, old, new ->
println("$old → $new")
}
prop = "abc"
prop = "xyz"
}
以上代碼在Kotlin 1.3 上編譯不過,因為在分析by后面的委托表達式時,不會考慮委托屬性的類型,因此會報類型錯誤。但是現(xiàn)在的kotlin 1.4-M1中,編譯器會正確推斷old和new參數(shù)類型為String?。
3.標準庫更改
3.1. 廢棄試驗性的協(xié)程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一起發(fā)布到了Bintray上。
3.2. 刪除已廢棄的mod操作符
另一個不建議使用的函數(shù)是數(shù)字類型的mod運算符,該運算符可計算除法運算后的余數(shù)。在Kotlin 1.1中,它被rem()函數(shù)取代。現(xiàn)在,將其從標準庫中完全刪除。
3.3. 廢棄從浮點類型到Byte和Short的轉(zhuǎn)換
標準庫中包含了一些將浮點類型的轉(zhuǎn)換為整數(shù)類型的方法,如:toInt(), toShort(), toByte()。但是由于數(shù)值范圍狹小且變量大小較小,將浮點數(shù)轉(zhuǎn)換為Short和Byte可能會導致意外結(jié)果。為了解決這個問題,在1.4-M1中,我們廢棄了Double和Float中的toShort()和toByte()方法。如果你仍然想吧浮點類型轉(zhuǎn)化為Short或者Byte,該怎么辦呢?那也好辦,進行兩步轉(zhuǎn)換,先將浮點類型轉(zhuǎn)為Int,然后再將Int轉(zhuǎn)為目標類型就可以了。
3.4. 通用的發(fā)射API
我們修改了通用反射API。現(xiàn)在,它包含所有三個目標平臺(JVM,JS,Native)上可用的成員,因此現(xiàn)在可以確保相同的代碼可在其中任何一個平臺上上工作了。
3.5. 用于Kotlin反射的Proguard配置
從1.4-M1開始,我們在kotlin-reflect.jar中嵌入了Kotlin Reflection的Proguard / R8配置, 有了這個更改,大多數(shù)使用了R8或者Proguard的Android項目在不用其他任何配置的情況下使用kotlin-reflect。你不再需要復制粘貼Kotlin反射的Proguard規(guī)則。但是請注意,你仍然需要明確列出所有要考慮反射的API。
4. Kotlin/JVM
從1.3.70版開始,Kotlin能夠在JVM字節(jié)碼(目標版本1.8+)中生成類型注解,以便它們在運行時可用。社區(qū)要求此功能已有一段時間,因為它使使用某些現(xiàn)有Java庫變得更加容易,并為新庫的作者提供了更多的擴展能力。
在以下示例中,可以在字節(jié)碼中發(fā)出String類型的@Foo批注,然后由庫代碼使用:
@Target(AnnotationTarget.TYPE)
annotation class Foo
class A {
fun foo(): @Foo String = "OK"
}
關(guān)于具體如何使用,可以看一下這篇博客:https://blog.jetbrains.com/kotlin/2020/03/kotlin-1-3-70-released/#kotlin-jvm
5. 其他一些改動
除了上面的一些改動之外,對于Kotlin/Js和Kotlin/iOS 平臺也有一些優(yōu)化和改進,大致列出來看一下:
5.1 Kotlin/JS
5.1.1. Gradle DSL 更改
在kotlin.js和multiplatformGradle插件中,引入了新的重要設(shè)置。在build.gradle.kts文件的目標塊內(nèi),如果您想在構(gòu)建過程中生成.js工件,則可以配置并使用produceExecutable()。
kotlin {
target {
useCommonJs()
produceExecutable()
browser {}
}
}
如果您正在編寫Kotlin / JS庫,則可以省略
ProduceExecutable()配置。當使用新的IR編譯器后端(有關(guān)此內(nèi)容的更多詳細信息,在下文中)時,省略此設(shè)置意味著將不會生成可執(zhí)行的JS文件(因此,構(gòu)建過程將運行得更快)。將在build / libs文件夾中生成一個klib文件,該文件可從其他Kotlin / JS項目使用,也可作為同一項目中的依賴項。如果您未明確指定
produceExecutable(),則默認情況下會發(fā)生這種情況。
使用produceExecutable()將生成可從JavaScript生態(tài)系統(tǒng)執(zhí)行的代碼,無論其具有自己的入口點還是作為JavaScript庫,這將生成實際的JavaScript文件,該文件可以在節(jié)點解釋器中運行,可以嵌入HTML頁面中并在瀏覽器中執(zhí)行,或用作JavaScript項目的依賴項。
5.1.2. 新后端
Kotlin 1.4-M1是第一個包含針對Kotlin / JS目標的新IR編譯器后端的版本。此后端是極大改進的基礎(chǔ),也是Kotlin / JS與JavaScript和TypeScript交互方式發(fā)生某些變化的決定性因素。以下突出顯示的幾個功能均針對新的IR編譯器后端。雖然默認情況下尚未啟用它,我們鼓勵你在項目中嘗試以下它。
(1)如何使用新的后端?
在gradle.properties配置文件中添加以下配置:
kotlin.js.compiler=ir // or both
如果需要為IR編譯器后端和默認后端生成庫,則可以選擇將此標志設(shè)置為both。
關(guān)于both 的作用請看下面的章節(jié)介紹。
(2)無二進制兼容
新的IR編譯器后端與原來默認的后端相比主要的變換是沒有二進制兼容,Kotlin / JS的兩個后端之間缺乏這種兼容性,這意味著使用新的IR編譯器后端創(chuàng)建的庫無法從默認后端使用,反之亦然。
(3)DCE 優(yōu)化
與默認后端相比,新的IR編譯器后端進行了很多優(yōu)化。生成的代碼與靜態(tài)分析器配合使用效果更好了,甚至可以通過Google的Closure Compiler從新的IR編譯器后端運行生成的代碼,并使用其高級優(yōu)化模式。
(4)支持聲明導出到JavaScript
現(xiàn)在,標記為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中,默認就支持范型了。但在某些情況下,這可能會破壞現(xiàn)有的調(diào)用Kotlin框架的Objective-C或Swift代碼。如果不想使用范型,請?zhí)砑?code>-Xno-objc-generics選項
binaries.framework {
freeCompilerArgs += "-Xno-objc-generics"
}
6.2. Objective-C/Swift 互操作中異常處理變化
在1.4中,我們略微更改了從Kotlin生成Swift API異常處理的方式。Kotlin和Swift的錯誤處理存在根本的不同,所有Kotlin異常均未經(jīng)檢查,而Swift僅檢查錯誤。因此,為了使Swift代碼感知異常,需使用@Throws注解標記Kotlin函數(shù),該注解指定潛在異常類的列表。
當編譯為Swift或Objective-C框架時,具有或正在繼承@Throws注解的函數(shù)在Objective-C中表示為NSError *處理方法,而在Swift中表示為throws方法。
6.3. 性能提升
我們一直在努力提高Kotlin / Native編譯和執(zhí)行的整體性能。1.4-M1中,我們?yōu)樘峁┝诵碌膶ο蠓峙淦?,在某些基準測試中,它的運行速度提高了兩倍。當前,新的分配器是實驗性的,默認情況下不使用。您可以使用-Xallocator = mimalloc切換至該選項。
7.總結(jié)
以上就是Kotlin1.4-M1的一些變化,其中最令我驚喜的一個功能是:終于支持Kotlin interface SAM 轉(zhuǎn)換了。其他的一些功能大家都可以去試一下,更多更詳細的信息請去官網(wǎng)了解,期待早點出release版吧!
