Kotlin 1.8.20 有什麼新功能
發布日期:2023 年 4 月 25 日](releases.md#release-details)
Kotlin 1.8.20 版本已發布,以下是其一些重要亮點:
- Kotlin K2 編譯器新功能更新
- 全新實驗性 Kotlin/Wasm 目標
- Gradle 中 JVM 增量編譯預設開啟
- Kotlin/Native 目標更新
- Kotlin Multiplatform 中 Gradle 複合建構預覽
- Xcode 中 Gradle 錯誤輸出改進
- 標準函式庫中 AutoCloseable 介面實驗性支援
- 標準函式庫中 Base64 編碼實驗性支援
您也可以在此影片中找到變更的簡要概述:
IDE 支援
支援 1.8.20 的 Kotlin 外掛程式適用於:
IDE | 支援版本 |
---|---|
IntelliJ IDEA | 2022.2.x, 2022.3.x, 2023.1.x |
Android Studio | Flamingo (222) |
為了正確下載 Kotlin 構件 (artifacts) 和依賴項,請配置 Gradle 設定 以使用 Maven Central 儲存庫。
Kotlin K2 編譯器新功能更新
Kotlin 團隊持續穩定 K2 編譯器。正如Kotlin 1.7.0 公告中所述,它仍處於 Alpha 階段。此版本為通往 K2 Beta 之路引入了更多改進。
從 1.8.20 版本開始,Kotlin K2 編譯器:
- 具備序列化外掛程式的預覽版本。
- 提供 JS IR 編譯器的 Alpha 支援。
- 引入了新語言版本 Kotlin 2.0的未來發布。
透過以下影片了解更多關於新編譯器及其優勢:
如何啟用 Kotlin K2 編譯器
若要啟用和測試 Kotlin K2 編譯器,請使用以下編譯器選項搭配新的語言版本:
-language-version 2.0
您可以在您的 build.gradle(.kts)
檔案中指定它:
kotlin {
sourceSets.all {
languageSettings {
languageVersion = "2.0"
}
}
}
先前的 -Xuse-k2
編譯器選項已棄用。
新 K2 編譯器的 Alpha 版本僅適用於 JVM 和 JS IR 專案。 它尚不支援 Kotlin/Native 或任何多平台專案。
提供您對新 K2 編譯器的意見回饋
我們非常感謝您的任何意見回饋!
- 直接向 Kotlin Slack 上的 K2 開發人員提供您的意見回饋 – 獲取邀請並加入 #k2-early-adopters 頻道。
- 在我們的問題追蹤器上報告您在使用新 K2 編譯器時遇到的任何問題。
- 啟用 Send usage statistics 選項,允許 JetBrains 收集關於 K2 使用情況的匿名數據。
語言
隨著 Kotlin 持續發展,我們在 1.8.20 版本中引入了新語言功能的預覽版本:
Enum 類別 values 函式的現代化高效能替代方案
此功能為實驗性。 它可能隨時被移除或更改。需要選擇啟用(詳情請見下文)。僅用於評估目的。 我們非常感謝您在 YouTrack 上提供相關意見回饋。
Enum 類別具有一個合成 (synthetic) 的 values()
函式,它會返回一個定義好的列舉常數陣列。然而,在 Kotlin 和 Java 中,使用陣列可能導致隱藏的效能問題。此外,大多數 API 使用集合 (collections),這需要最終轉換。為了解決這些問題,我們為 Enum 類別引入了 entries
屬性,它應該取代 values()
函式使用。當呼叫時,entries
屬性會返回一個預先分配好的不可變 (immutable) 列舉常數列表。
values()
函式仍然受支援,但我們建議您改用entries
屬性。
enum class Color(val colorName: String, val rgb: String) {
RED("Red", "#FF0000"),
ORANGE("Orange", "#FF7F00"),
YELLOW("Yellow", "#FFFF00")
}
@OptIn(ExperimentalStdlibApi::class)
fun findByRgb(rgb: String): Color? = Color.entries.find { it.rgb == rgb }
如何啟用 entries 屬性
若要試用此功能,請使用 @OptIn(ExperimentalStdlibApi)
選擇啟用,並啟用 -language-version 1.9
編譯器選項。在 Gradle 專案中,您可以透過將以下內容添加到 build.gradle(.kts)
檔案中來實現:
tasks
.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask<*>>()
.configureEach {
compilerOptions
.languageVersion
.set(
org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9
)
}
tasks
.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask.class)
.configureEach {
compilerOptions.languageVersion =
org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9
}
從 IntelliJ IDEA 2023.1 開始,如果您已選擇啟用此功能,則相應的 IDE 檢查將通知您將
values()
轉換為entries
並提供快速修復。
有關該提案的更多資訊,請參閱 KEEP 說明。
用於與 data class 對稱的 data objects 預覽
Data objects 允許您宣告具有單例 (singleton) 語義和簡潔 toString()
表示的物件。在此程式碼片段中,您可以看到如何透過在物件宣告中添加 data
關鍵字來提高其 toString()
輸出的可讀性:
package org.example
object MyObject
data object MyDataObject
fun main() {
println(MyObject) // org.example.MyObject@1f32e575
println(MyDataObject) // MyDataObject
}
特別對於 sealed
繼承層級 (例如 sealed class
或 sealed interface
繼承層級),data objects
是絕佳的選擇,因為它們可以方便地與 data class
宣告一起使用。在此程式碼片段中,將 EndOfFile
宣告為 data object
而不是普通的 object
意味著它將獲得一個漂亮的 toString
,而無需手動覆寫。這保持了與附帶的 data class 定義的對稱性。
sealed interface ReadResult
data class Number(val number: Int) : ReadResult
data class Text(val text: String) : ReadResult
data object EndOfFile : ReadResult
fun main() {
println(Number(7)) // Number(number=7)
println(EndOfFile) // EndOfFile
}
data objects 的語義
自從在 Kotlin 1.7.20 中首次預覽以來,data objects 的語義已得到完善。編譯器現在會自動為它們生成許多便利函式:
toString
data object 的 toString()
函式會返回物件的簡單名稱:
data object MyDataObject {
val x: Int = 3
}
fun main() {
println(MyDataObject) // MyDataObject
}
equals 和 hashCode
data object 的 equals()
函式確保所有與您的 data object
具有相同類型的物件都被視為相等。在大多數情況下,您在執行時只會有一個資料物件實例 (畢竟,data object
宣告了一個單例)。然而,在執行時產生另一個相同類型的物件的邊緣情況下 (例如,透過 java.lang.reflect
的平台反射,或使用在底層使用此 API 的 JVM 序列化函式庫),這可以確保這些物件被視為相等。
請務必僅以結構方式 (使用 ==
運算子) 比較 data objects
,切勿以引用方式 (使用 ===
運算子)。這有助於避免在執行時存在多個 data object 實例時的陷阱。以下程式碼片段說明了此特定邊緣情況:
import java.lang.reflect.Constructor
data object MySingleton
fun main() {
val evilTwin = createInstanceViaReflection()
println(MySingleton) // MySingleton
println(evilTwin) // MySingleton
// Even when a library forcefully creates a second instance of MySingleton, its `equals` method returns true:
println(MySingleton == evilTwin) // true
// Do not compare data objects via ===.
println(MySingleton === evilTwin) // false
}
fun createInstanceViaReflection(): MySingleton {
// Kotlin reflection does not permit the instantiation of data objects.
// This creates a new MySingleton instance "by force" (i.e., Java platform reflection)
// Don't do this yourself!
return (MySingleton.javaClass.declaredConstructors[0].apply { isAccessible = true } as Constructor<MySingleton>).newInstance()
}
生成的 hashCode()
函式的行為與 equals()
函式一致,因此 data object 的所有執行時實例都具有相同的雜湊碼 (hash code)。
data objects 沒有 copy 和 componentN 函式
雖然 data object
和 data class
宣告經常一起使用並有一些相似之處,但有些函式不會為 data object
生成:
由於 data object
宣告旨在用作單例物件,因此不會生成 copy()
函式。單例模式限制了類別的實例化為單一實例,允許創建實例的副本將違反該限制。
此外,與 data class
不同,data object
沒有任何資料屬性。由於嘗試解構此類物件沒有意義,因此不會生成 componentN()
函式。
我們非常感謝您在 YouTrack 上提供有關此功能的意見回饋。
如何啟用 data objects 預覽
若要試用此功能,請啟用 -language-version 1.9
編譯器選項。在 Gradle 專案中,您可以透過將以下內容添加到 build.gradle(.kts)
檔案中來實現:
tasks
.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask<*>>()
.configureEach {
compilerOptions
.languageVersion
.set(
org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9
)
}
tasks
.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask.class)
.configureEach {
compilerOptions.languageVersion =
org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9
}
解除 inline classes 中帶有函式主體的次級建構函式限制預覽
此功能為實驗性。它可能隨時被移除或更改。 需要選擇啟用(詳情請見下文)。僅用於評估目的。我們非常感謝您在 YouTrack 上提供相關意見回饋。
Kotlin 1.8.20 解除了內聯類別 (inline classes) 中使用帶有函式主體的次級建構函式的限制。
內聯類別過去只允許公開的主建構函式,不能帶有 init
區塊或次級建構函式,以便具有清晰的初始化語義。因此,不可能封裝底層值或創建表示某些受限值的內聯類別。
當 Kotlin 1.4.30 解除 init
區塊的限制時,這些問題得到了解決。現在,我們更進一步,在預覽模式下允許帶有函式主體的次級建構函式:
@JvmInline
value class Person(private val fullName: String) {
// Allowed since Kotlin 1.4.30:
init {
check(fullName.isNotBlank()) {
"Full name shouldn't be empty"
}
}
// Preview available since Kotlin 1.8.20:
constructor(name: String, lastName: String) : this("$name $lastName") {
check(lastName.isNotBlank()) {
"Last name shouldn't be empty"
}
}
}
如何啟用帶有函式主體的次級建構函式
若要試用此功能,請啟用 -language-version 1.9
編譯器選項。在 Gradle 專案中,您可以透過將以下內容添加到您的 build.gradle(.kts)
中來實現:
tasks
.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask<*>>()
.configureEach {
compilerOptions
.languageVersion
.set(
org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9
)
}
tasks
.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask.class)
.configureEach {
compilerOptions.languageVersion =
org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9
}
我們鼓勵您試用此功能,並在 YouTrack 中提交所有報告,以幫助我們在 Kotlin 1.9.0 中使其成為預設設定。
在 此 KEEP 中了解更多關於 Kotlin 內聯類別的開發。
全新 Kotlin/Wasm 目標
Kotlin/Wasm (Kotlin WebAssembly) 在此版本中進入實驗性階段。Kotlin 團隊認為 WebAssembly 是一項很有前途的技術,並希望找到更好的方法讓您使用它並獲得 Kotlin 的所有優勢。
WebAssembly 二進位格式獨立於平台,因為它使用自己的虛擬機器運行。幾乎所有現代瀏覽器都已支援 WebAssembly 1.0。若要設定運行 WebAssembly 的環境,您只需啟用 Kotlin/Wasm 目標的實驗性垃圾收集模式。您可以在此處找到詳細說明:如何啟用 Kotlin/Wasm。
我們想強調全新 Kotlin/Wasm 目標的以下優勢:
- 與
wasm32
Kotlin/Native 目標相比,編譯速度更快,因為 Kotlin/Wasm 不需要使用 LLVM。 - 相較於
wasm32
目標,與 JS 的互通性以及與瀏覽器的整合更為容易,這歸功於 Wasm 垃圾收集。 - 與 Kotlin/JS 和 JavaScript 相比,應用程式啟動速度潛在更快,因為 Wasm 具有緊湊且易於解析的位元組碼。
- 與 Kotlin/JS 和 JavaScript 相比,應用程式執行時效能更高,因為 Wasm 是一種靜態型別語言。
從 1.8.20 版本開始,您可以在實驗性專案中使用 Kotlin/Wasm。我們為 Kotlin/Wasm 開箱即用提供了 Kotlin 標準函式庫 (stdlib
) 和測試函式庫 (kotlin.test
)。IDE 支援將在未來版本中添加。
在此 YouTube 影片中了解更多關於 Kotlin/Wasm 的資訊。
如何啟用 Kotlin/Wasm
若要啟用和測試 Kotlin/Wasm,請更新您的 build.gradle.kts
檔案:
plugins {
kotlin("multiplatform") version "1.8.20"
}
kotlin {
wasm {
binaries.executable()
browser {
}
}
sourceSets {
val commonMain by getting
val commonTest by getting {
dependencies {
implementation(kotlin("test"))
}
}
val wasmMain by getting
val wasmTest by getting
}
}
若要運行 Kotlin/Wasm 專案,您需要更新目標環境的設定:
對於版本 109:
使用
--js-flags=--experimental-wasm-gc
命令列參數運行應用程式。對於版本 110 或更高版本:
- 在瀏覽器中前往
chrome://flags/#enable-webassembly-garbage-collection
。 - 啟用 WebAssembly Garbage Collection。
- 重新啟動瀏覽器。
- 在瀏覽器中前往
對於版本 109 或更高版本:
- 在瀏覽器中前往
about:config
。 - 啟用
javascript.options.wasm_function_references
和javascript.options.wasm_gc
選項。 - 重新啟動瀏覽器。
對於版本 109 或更高版本:
使用 --js-flags=--experimental-wasm-gc
命令列參數運行應用程式。
提供您對 Kotlin/Wasm 的意見回饋
我們非常感謝您的任何意見回饋!
- 直接向 Kotlin Slack 的開發人員提供您的意見回饋 – 獲取邀請並加入 #webassembly 頻道。
- 在這個 YouTrack 問題上報告您在使用 Kotlin/Wasm 時遇到的任何問題。
Kotlin/JVM
Kotlin 1.8.20 引入了 Java 合成屬性參考預覽以及預設支援 kapt 存根 (stub) 生成任務中的 JVM IR 後端。
Java 合成屬性參考預覽
Kotlin 1.8.20 引入了建立 Java 合成屬性參考的功能,例如,對於以下 Java 程式碼:
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
Kotlin 一直允許您編寫 person.age
,其中 age
是一個合成屬性。現在,您還可以創建對 Person::age
和 person::age
的參考。對於 name
也同樣適用。
val persons = listOf(Person("Jack", 11), Person("Sofie", 12), Person("Peter", 11))
persons
// Call a reference to Java synthetic property:
.sortedBy(Person::age)
// Call Java getter via the Kotlin property syntax:
.forEach { person -> println(person.name) }
如何啟用 Java 合成屬性參考
若要試用此功能,請啟用 -language-version 1.9
編譯器選項。在 Gradle 專案中,您可以透過將以下內容添加到您的 build.gradle(.kts)
中來實現:
tasks
.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask<*>>()
.configureEach {
compilerOptions
.languageVersion
.set(
org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9
)
}
tasks
.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask.class)
.configureEach {
compilerOptions.languageVersion =
org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9
}
預設支援 kapt 存根生成任務中的 JVM IR 後端
在 Kotlin 1.7.20 中,我們引入了對 kapt 存根生成任務中 JVM IR 後端的支援。從此版本開始,此支援預設啟用。您不再需要在 gradle.properties
中指定 kapt.use.jvm.ir=true
來啟用它。我們非常感謝您在 YouTrack 上提供有關此功能的意見回饋。
Kotlin/Native
Kotlin 1.8.20 包含了對支援的 Kotlin/Native 目標、與 Objective-C 的互通性以及 CocoaPods Gradle 外掛程式改進等方面的變更:
- Kotlin/Native 目標更新
- 舊有記憶體管理器棄用
- 支援帶有 @import 指令的 Objective-C 標頭
- CocoaPods Gradle 外掛程式支援僅連結模式
- 在 UIKit 中將 Objective-C 擴充功能作為類別成員匯入
- 編譯器中編譯器快取管理重新實作
- Cocoapods Gradle 外掛程式中
useLibraries()
棄用
Kotlin/Native 目標更新
Kotlin 團隊決定重新審視 Kotlin/Native 支援的目標列表,將它們分為多個層級,並從 Kotlin 1.8.20 開始棄用其中一些目標。有關支援和已棄用目標的完整列表,請參閱 Kotlin/Native 目標支援部分。
以下目標已在 Kotlin 1.8.20 中棄用,並將在 1.9.20 中移除:
iosArm32
watchosX86
wasm32
mingwX86
linuxArm32Hfp
linuxMips32
linuxMipsel32
至於其餘目標,現在有三種支援層級,取決於 Kotlin/Native 編譯器中目標的支援和測試情況。目標可以移動到不同的層級。例如,我們將盡力在未來為 iosArm64
提供全面支援,因為它對 Kotlin Multiplatform 至關重要。
如果您是函式庫作者,這些目標層級可以幫助您決定在 CI 工具上測試哪些目標以及跳過哪些目標。Kotlin 團隊在開發官方 Kotlin 函式庫時將採用相同的方法,例如 kotlinx.coroutines。
查看我們的部落格文章以了解這些變更的原因。
舊有記憶體管理器棄用
從 1.8.20 開始,舊有記憶體管理器已被棄用,並將在 1.9.20 中移除。新記憶體管理器已在 1.7.20 中預設啟用,並持續獲得穩定性更新和效能改進。
如果您仍在使用舊有記憶體管理器,請從 gradle.properties
中移除 kotlin.native.binary.memoryModel=strict
選項,並遵循我們的遷移指南進行必要的變更。
新記憶體管理器不支援 wasm32
目標。該目標也從此版本開始棄用,並將在 1.9.20 中移除。
支援帶有 @import 指令的 Objective-C 標頭
此功能為實驗性。 它可能隨時被移除或更改。需要選擇啟用(詳情請見下文)。僅用於評估目的。 我們非常感謝您在 YouTrack 上提供相關意見回饋。
Kotlin/Native 現在可以匯入帶有 @import
指令的 Objective-C 標頭。此功能對於使用具有自動生成 Objective-C 標頭的 Swift 函式庫或用 Swift 編寫的 CocoaPods 依賴項的類別很有用。
以前,cinterop 工具無法分析透過 @import
指令依賴於 Objective-C 模組的標頭。原因是它缺乏對 -fmodules
選項的支援。
從 Kotlin 1.8.20 開始,您可以將 Objective-C 標頭與 @import
一起使用。為此,請在定義檔案中將 -fmodules
選項作為 compilerOpts
傳遞給編譯器。如果您使用 CocoaPods 整合,請在 pod()
函式的配置區塊中指定 cinterop 選項,如下所示:
kotlin {
ios()
cocoapods {
summary = "CocoaPods test library"
homepage = "https://github.com/JetBrains/kotlin"
ios.deploymentTarget = "13.5"
pod("PodName") {
extraOpts = listOf("-compiler-option", "-fmodules")
}
}
}
這是一個備受期待的功能,我們歡迎您在 YouTrack 上提供相關意見回饋,以幫助我們在未來版本中將其設為預設。
CocoaPods Gradle 外掛程式支援僅連結模式
藉由 Kotlin 1.8.20,您可以將 Pod 依賴項與動態框架僅用於連結,而無需生成 cinterop 綁定。當 cinterop 綁定已生成時,這會非常方便。
考慮一個包含 2 個模組的專案:一個函式庫和一個應用程式。該函式庫依賴於一個 Pod,但不產生框架,只產生 .klib
。該應用程式依賴於該函式庫並產生一個動態框架。在這種情況下,您需要將此框架與函式庫所依賴的 Pod 連結起來,但您不需要 cinterop 綁定,因為它們已經為函式庫生成了。
若要啟用此功能,請在添加對 Pod 的依賴項時使用 linkOnly
選項或建構器 (builder) 屬性:
cocoapods {
summary = "CocoaPods test library"
homepage = "https://github.com/JetBrains/kotlin"
pod("Alamofire", linkOnly = true) {
version = "5.7.0"
}
}
如果您將此選項與靜態框架一起使用,它將完全移除 Pod 依賴項,因為 Pod 不用於靜態框架連結。
在 UIKit 中將 Objective-C 擴充功能作為類別成員匯入
自 Xcode 14.1 以來,Objective-C 類別中的一些方法已移至類別成員。這導致生成了不同的 Kotlin API,並且這些方法被匯入為 Kotlin 擴充功能而不是方法。
您在使用 UIKit 覆寫方法時可能遇到因此產生的問題。例如,在 Kotlin 中對 UIView 進行子類化時,不可能覆寫 drawRect()
或 layoutSubviews()
方法。
自 1.8.20 起,在與 NSView 和 UIView 類別相同的標頭中宣告的類別成員會作為這些類別的成員匯入。這意味著從 NSView 和 UIView 繼承的方法可以像任何其他方法一樣輕鬆覆寫。
如果一切順利,我們計劃預設為所有 Objective-C 類別啟用此行為。
編譯器中編譯器快取管理重新實作
為了加速編譯器快取的演進,我們已將編譯器快取管理從 Kotlin Gradle 外掛程式移至 Kotlin/Native 編譯器。這解鎖了幾項重要改進的工作,包括與編譯時間和編譯器快取靈活性相關的改進。
如果您遇到問題需要恢復舊有行為,請使用 kotlin.native.cacheOrchestration=gradle
Gradle 屬性。
我們非常感謝您在 YouTrack 上提供相關意見回饋。
Cocoapods Gradle 外掛程式中 useLibraries()
棄用
Kotlin 1.8.20 開始了 useLibraries()
函式的棄用週期,該函式用於靜態函式庫的 CocoaPods 整合。
我們引入 useLibraries()
函式以允許依賴包含靜態函式庫的 Pod。隨著時間的推移,這種情況變得非常罕見。大多數 Pod 透過原始碼分發,而 Objective-C 框架或 XCFrameworks 是二進位分發的常見選擇。
由於此函式不受歡迎,並且它造成了複雜化 Kotlin CocoaPods Gradle 外掛程式開發的問題,我們決定將其棄用。
有關框架和 XCFrameworks 的更多資訊,請參閱 建構最終原生二進位檔。
Kotlin 多平台
Kotlin 1.8.20 致力於透過對 Kotlin 多平台進行以下更新來改善開發人員體驗:
設定原始碼集層級的新方法
原始碼集層級的新方法為實驗性。 它可能在未來的 Kotlin 版本中未經事先通知而更改。需要選擇啟用(詳情請見下文)。 我們非常感謝您在 YouTrack 上提供相關意見回饋。
Kotlin 1.8.20 提供了一種在多平台專案中設定原始碼集層級的新方法 − 預設目標層級。新方法旨在取代像 ios
這樣的目標捷徑,這些捷徑有其設計缺陷。
預設目標層級背後的理念很簡單:您明確宣告專案編譯到的所有目標,Kotlin Gradle 外掛程式會根據指定的目標自動創建共享原始碼集。
設定您的專案
考慮這個簡單的多平台行動應用程式範例:
@OptIn(ExperimentalKotlinGradlePluginApi::class)
kotlin {
// Enable the default target hierarchy:
targetHierarchy.default()
android()
iosArm64()
iosSimulatorArm64()
}
您可以將預設目標層級視為所有可能目標及其共享原始碼集的範本。當您在程式碼中宣告最終目標 android
、iosArm64
和 iosSimulatorArm64
時,Kotlin Gradle 外掛程式會從範本中找到合適的共享原始碼集並為您創建它們。結果層級如下所示:
綠色原始碼集實際上已在專案中創建並存在,而預設範本中的灰色原始碼集則被忽略。正如您所見,Kotlin Gradle 外掛程式並未創建 watchos
原始碼集,例如,因為專案中沒有 watchOS 目標。
如果您添加一個 watchOS 目標,例如 watchosArm64
,則會創建 watchos
原始碼集,並且來自 apple
、native
和 common
原始碼集的程式碼也將編譯到 watchosArm64
。
您可以在文件中找到預設目標層級的完整方案。
在此範例中,
apple
和native
原始碼集僅編譯到iosArm64
和iosSimulatorArm64
目標。 因此,儘管它們的名字如此,它們仍然可以存取完整的 iOS API。 這對於像native
這樣的原始碼集可能違反直覺,因為您可能期望只有在所有原生目標上可用的 API 才能在此原始碼集中存取。此行為未來可能會改變。
為何取代捷徑
創建原始碼集層級可能很冗長、容易出錯,並且對初學者不友好。我們之前的解決方案是引入像 ios
這樣的捷徑,為您創建層級的一部分。然而,事實證明使用捷徑存在一個很大的設計缺陷:它們難以更改。
以 ios
捷徑為例。它只創建 iosArm64
和 iosX64
目標,這可能會令人困惑,並且在使用需要 iosSimulatorArm64
目標的 M1 主機上可能導致問題。然而,添加 iosSimulatorArm64
目標對於使用者專案來說可能是一個非常破壞性的改變:
iosMain
原始碼集中使用的所有依賴項都必須支援iosSimulatorArm64
目標;否則,依賴項解析將失敗。- 在添加新目標時,
iosMain
中使用的一些原生 API 可能會消失 (儘管在iosSimulatorArm64
的情況下不太可能發生)。 - 在某些情況下,例如當您在基於 Intel 的 MacBook 上編寫一個小型個人專案時,您甚至可能不需要此更改。
很明顯,捷徑未能解決配置層級的問題,這就是我們在某個時候停止添加新捷徑的原因。
預設目標層級乍看之下可能與捷徑相似,但它們有一個關鍵區別:使用者必須明確指定目標集。此集定義了您的專案如何編譯和發布,以及它如何參與依賴項解析。由於此集是固定的,因此 Kotlin Gradle 外掛程式的預設配置更改應會顯著減少生態系統中的困擾,並且提供工具輔助遷移將變得更加容易。
如何啟用預設層級
這項新功能是實驗性。對於 Kotlin Gradle 建構腳本,您需要使用 @OptIn(ExperimentalKotlinGradlePluginApi::class)
選擇啟用。
有關更多資訊,請參閱層級專案結構。
留下意見回饋
這對多平台專案來說是一個重大變革。我們非常感謝您的意見回饋,以幫助使其更加完善。
Kotlin 多平台中 Gradle 複合建構支援的預覽
此功能自 Kotlin Gradle 外掛程式 1.8.20 起已在 Gradle 建構中支援。對於 IDE 支援,請使用 IntelliJ IDEA 2023.1 Beta 2 (231.8109.2) 或更高版本,以及帶有任何 Kotlin IDE 外掛程式的 Kotlin Gradle 外掛程式 1.8.20。
從 1.8.20 開始,Kotlin 多平台支援 Gradle 複合建構 (composite builds)。複合建構允許您將單獨專案或同一專案部分中的建構包含到單一建構中。
由於一些技術挑戰,Kotlin 多平台與 Gradle 複合建構的搭配使用僅得到部分支援。Kotlin 1.8.20 包含了改進支援的預覽,該支援應該適用於更多種類的專案。若要試用它,請將以下選項添加到您的 gradle.properties
中:
kotlin.mpp.import.enableKgpDependencyResolution=true
此選項啟用新匯入模式的預覽。除了支援複合建構外,它還提供了多平台專案中更流暢的匯入體驗,因為我們已包含主要的錯誤修復和改進,以使匯入更穩定。
已知問題
它仍是一個需要進一步穩定的預覽版本,您可能會在匯入過程中遇到一些問題。以下是我們計劃在 Kotlin 1.8.20 正式發布前修復的一些已知問題:
- IntelliJ IDEA 2023.1 EAP 尚無可用的 Kotlin 1.8.20 外掛程式。儘管如此,您仍然可以將 Kotlin Gradle 外掛程式版本設定為 1.8.20,並在此 IDE 中試用複合建構。
- 如果您的專案包含指定了
rootProject.name
的建構,複合建構可能無法解析 Kotlin 中繼資料 (metadata)。有關解決方案和詳細資訊,請參閱此 YouTrack 問題。
我們鼓勵您試用此功能,並在 YouTrack 上提交所有報告,以幫助我們在 Kotlin 1.9.0 中使其成為預設設定。
Xcode 中 Gradle 錯誤輸出改進
如果您在 Xcode 中建構多平台專案時遇到問題,您可能會遇到「Command PhaseScriptExecution failed with a nonzero exit code」錯誤。此訊息表示 Gradle 呼叫失敗,但對於嘗試偵測問題並不是很有幫助。
從 Kotlin 1.8.20 開始,Xcode 可以解析 Kotlin/Native 編譯器的輸出。此外,如果 Gradle 建構失敗,您將在 Xcode 中看到來自根本原因例外情況的額外錯誤訊息。在大多數情況下,這將有助於識別根本問題。
對於 Xcode 整合的標準 Gradle 任務,例如可將多平台專案中的 iOS 框架連接到 Xcode 中的 iOS 應用程式的 embedAndSignAppleFrameworkForXcode
,此新行為預設啟用。它也可以透過 kotlin.native.useXcodeMessageStyle
Gradle 屬性啟用(或停用)。
Kotlin/JavaScript
Kotlin 1.8.20 改變了 TypeScript 定義的生成方式。它還包含一項旨在改善您的偵錯體驗:
從 Gradle 外掛程式中移除 Dukat 整合
在 Kotlin 1.8.20 中,我們已從 Kotlin/JavaScript Gradle 外掛程式中移除了我們的實驗性 Dukat 整合。Dukat 整合支援將 TypeScript 宣告檔案 (.d.ts
) 自動轉換為 Kotlin 外部宣告。
您仍然可以透過使用我們的 Dukat 工具將 TypeScript 宣告檔案 (.d.ts
) 轉換為 Kotlin 外部宣告。
Dukat 工具為實驗性。 它可能隨時被移除或更改。
原始碼映射中 Kotlin 變數和函式名稱
為了幫助偵錯,我們引入了將您在 Kotlin 程式碼中宣告的變數和函式名稱添加到原始碼映射 (source maps) 中的功能。在 1.8.20 之前,這些名稱在原始碼映射中不可用,因此在偵錯器中,您總是看到生成 JavaScript 的變數和函式名稱。
您可以透過在 Gradle 檔案 build.gradle.kts
中使用 sourceMapNamesPolicy
或 -source-map-names-policy
編譯器選項來配置添加的內容。下表列出了可能的設定:
設定 | 描述 | 範例輸出 |
---|---|---|
simple-names | 添加變數名稱和簡單函式名稱。(預設) | main |
fully-qualified-names | 添加變數名稱和完全限定函式名稱。 | com.example.kjs.playground.main |
no | 不添加任何變數或函式名稱。 | N/A |
以下是 build.gradle.kts
檔案中的配置範例:
tasks.withType<org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile>().configureEach {
compilercompileOptions.sourceMapNamesPolicy.set(org.jetbrains.kotlin.gradle.dsl.JsSourceMapNamesPolicy.SOURCE_MAP_NAMES_POLICY_FQ_NAMES) // or SOURCE_MAP_NAMES_POLICY_NO, or SOURCE_MAP_NAMES_POLICY_SIMPLE_NAMES
}
像基於 Chromium 的瀏覽器中提供的偵錯工具可以從您的原始碼映射中獲取原始 Kotlin 名稱,以提高堆疊追蹤 (stack trace) 的可讀性。祝您偵錯愉快!
變數和函式名稱在原始碼映射中的添加為實驗性。 它可能隨時被移除或更改。
選擇啟用 TypeScript 定義檔生成
以前,如果您有一個產生可執行檔 (binaries.executable()
) 的專案,Kotlin/JS IR 編譯器會收集任何標記有 @JsExport
的頂層宣告,並自動在 .d.ts
檔案中生成 TypeScript 定義。
由於這並非對每個專案都有用,我們在 Kotlin 1.8.20 中更改了此行為。如果您想要生成 TypeScript 定義,您必須在 Gradle 建構檔案中明確配置此功能。將 generateTypeScriptDefinitions()
添加到您的 build.gradle.kts.file
的 js
部分中。例如:
kotlin {
js {
binaries.executable()
browser {
}
generateTypeScriptDefinitions()
}
}
TypeScript 定義 (
d.ts
) 的生成為實驗性。它可能隨時被移除或更改。
Gradle
Kotlin 1.8.20 完全兼容 Gradle 6.8 到 7.6,除了多平台外掛程式中的一些特殊情況。您也可以使用直到最新 Gradle 版本的 Gradle 版本,但如果您這樣做,請記住您可能會遇到棄用警告或某些新的 Gradle 功能可能無法正常運作。
此版本帶來了以下變更:
- Gradle 外掛程式版本新對齊方式
- Gradle 中 JVM 增量編譯預設開啟
- 編譯任務輸出精確備份
- 所有 Gradle 版本 Kotlin/JVM 任務惰性創建
- 編譯任務 destinationDirectory 的非預設位置
- 選擇不向 HTTP 統計服務報告編譯器參數的能力
Gradle 外掛程式版本新對齊方式
Gradle 提供了一種方法來確保必須協同工作的依賴項在版本上始終保持一致。Kotlin 1.8.20 也採用了這種方法。它預設啟用,因此您無需更改或更新配置即可啟用它。此外,您不再需要採用此解決方法來解析 Kotlin Gradle 外掛程式的傳遞依賴項。
我們非常感謝您在 YouTrack 上提供有關此功能的意見回饋。
Gradle 中 JVM 增量編譯預設開啟
自 Kotlin 1.7.0 起可用的增量編譯新方法現在預設啟用。您不再需要在 gradle.properties
中指定 kotlin.incremental.useClasspathSnapshot=true
來啟用它。
我們非常感謝您對此提供意見回饋。您可以在 YouTrack 中提交問題。
編譯任務輸出精確備份
編譯任務輸出精確備份為實驗性。 若要使用它,請將
kotlin.compiler.preciseCompilationResultsBackup=true
添加到gradle.properties
。 我們非常感謝您在 YouTrack 上提供相關意見回饋。
從 Kotlin 1.8.20 開始,您可以啟用精確備份,只有 Kotlin 在增量編譯中重新編譯的類別才會被備份。完整備份和精確備份都有助於在編譯錯誤後再次增量運行建構。精確備份還比完整備份節省建構時間。在大型專案中,或者如果許多任務正在進行備份,特別是當專案位於緩慢的 HDD 上時,完整備份可能會花費明顯的建構時間。
此最佳化是實驗性的。您可以透過將 kotlin.compiler.preciseCompilationResultsBackup
Gradle 屬性添加到 gradle.properties
檔案中來啟用它:
kotlin.compiler.preciseCompilationResultsBackup=true
JetBrains 中精確備份使用範例
在以下圖表中,您可以看到與完整備份相比使用精確備份的範例:
第一和第二張圖表顯示了 Kotlin 專案中精確備份如何影響 Kotlin Gradle 外掛程式的建構:
- 在對許多模組所依賴的模組進行小的 ABI 變更後 – 添加一個新的公共方法。
- 在對沒有其他模組依賴的模組進行小的非 ABI 變更後 – 添加一個私有函式。
第三張圖表顯示了 Space 專案中的精確備份如何影響在對許多模組所依賴的 Kotlin/JS 模組進行小的非 ABI 變更後 – 添加一個私有函式 – 建構網頁前端。
這些測量是在配備 Apple M1 Max CPU 的電腦上進行的;不同電腦會產生略有不同的結果。影響效能的因素包括但不限於:
- Kotlin 守護程序 (daemon) 和 Gradle 守護程序 的熱度。
- 磁碟的速度快慢。
- CPU 型號及其忙碌程度。
- 哪些模組受到變更影響以及這些模組的大小。
- 變更是 ABI 還是非 ABI。
使用建構報告評估最佳化
為了估計最佳化對您的專案和場景在您的電腦上的影響,您可以使用 Kotlin 建構報告。透過將以下屬性添加到您的 gradle.properties
檔案中來啟用文字檔案格式的報告:
kotlin.build.report.output=file
以下是啟用精確備份之前報告相關部分的範例:
Task ':kotlin-gradle-plugin:compileCommonKotlin' finished in 0.59 s
<...>
Time metrics:
Total Gradle task time: 0.59 s
Task action before worker execution: 0.24 s
Backup output: 0.22 s // 請注意這個數字
<...>
以下是啟用精確備份之後報告相關部分的範例:
Task ':kotlin-gradle-plugin:compileCommonKotlin' finished in 0.46 s
<...>
Time metrics:
Total Gradle task time: 0.46 s
Task action before worker execution: 0.07 s
Backup output: 0.05 s // 時間已減少
Run compilation in Gradle worker: 0.32 s
Clear jar cache: 0.00 s
Precise backup output: 0.00 s // 與精確備份相關
Cleaning up the backup stash: 0.00 s // 與精確備份相關
<...>
所有 Gradle 版本 Kotlin/JVM 任務惰性創建
對於在 Gradle 7.3+ 上使用 org.jetbrains.kotlin.gradle.jvm
外掛程式的專案,Kotlin Gradle 外掛程式不再急切地創建和配置任務 compileKotlin
。在較低的 Gradle 版本上,它只是簡單地註冊所有任務,並且在試運行時不配置它們。現在在使用 Gradle 7.3+ 時,也採用相同的行為。
編譯任務 destinationDirectory 的非預設位置
如果您執行以下任一操作,請使用一些額外程式碼更新您的建構腳本:
- 覆寫 Kotlin/JVM
KotlinJvmCompile
/KotlinCompile
任務的destinationDirectory
位置。 - 使用已棄用的 Kotlin/JS/非 IR 變體並覆寫
Kotlin2JsCompile
任務的destinationDirectory
。
您需要在 JAR 檔案中明確將 sourceSets.main.kotlin.classesDirectories
添加到 sourceSets.main.outputs
:
tasks.jar(type: Jar) {
from sourceSets.main.outputs
from sourceSets.main.kotlin.classesDirectories
}
選擇不向 HTTP 統計服務報告編譯器參數的能力
您現在可以控制 Kotlin Gradle 外掛程式是否應在 HTTP 建構報告中包含編譯器參數。有時,您可能不需要外掛程式報告這些參數。如果專案包含許多模組,其報告中的編譯器參數可能非常龐大且不那麼有用。現在有一種方法可以禁用它,從而節省記憶體。在您的 gradle.properties
或 local.properties
中,使用 kotlin.build.report.include_compiler_arguments=(true|false)
屬性。
我們非常感謝您在 YouTrack 上提供有關此功能的意見回饋。
標準函式庫
Kotlin 1.8.20 添加了各種新功能,其中一些對於 Kotlin/Native 開發特別有用:
AutoCloseable 介面支援
新的
AutoCloseable
介面為實驗性,若要使用它,您需要使用@OptIn(ExperimentalStdlibApi::class)
或編譯器參數-opt-in=kotlin.ExperimentalStdlibApi
選擇啟用。
AutoCloseable
介面已添加到通用標準函式庫中,以便您可以為所有函式庫使用一個通用介面來關閉資源。在 Kotlin/JVM 中,AutoCloseable
介面是 java.lang.AutoClosable
的別名。
此外,現在包含了擴充函式 use()
,它在選定的資源上執行給定的區塊函式,然後正確關閉它,無論是否拋出例外。
通用標準函式庫中沒有任何實作 AutoCloseable
介面的公共類別。在下面的範例中,我們定義了 XMLWriter
介面,並假設存在一個實作它的資源。例如,此資源可能是一個打開檔案、寫入 XML 內容然後關閉它的類別。
interface XMLWriter : AutoCloseable {
fun document(encoding: String, version: String, content: XMLWriter.() -> Unit)
fun element(name: String, content: XMLWriter.() -> Unit)
fun attribute(name: String, value: String)
fun text(value: String)
}
fun writeBooksTo(writer: XMLWriter) {
writer.use { xml ->
xml.document(encoding = "UTF-8", version = "1.0") {
element("bookstore") {
element("book") {
attribute("category", "fiction")
element("title") { text("Harry Potter and the Prisoner of Azkaban") }
element("author") { text("J. K. Rowling") }
element("year") { text("1999") }
element("price") { text("29.99") }
}
element("book") {
attribute("category", "programming")
element("title") { text("Kotlin in Action") }
element("author") { text("Dmitry Jemerov") }
element("author") { text("Svetlana Isakova") }
element("year") { text("2017") }
element("price") { text("25.19") }
}
}
}
}
}
Base64 編碼支援
新的編碼和解碼功能為實驗性, 若要使用它,您需要使用
@OptIn(ExperimentalEncodingApi::class)
或 編譯器參數-opt-in=kotlin.io.encoding.ExperimentalEncodingApi
選擇啟用。
我們添加了對 Base64 編碼和解碼的支援。我們提供了 3 個類別實例,每個實例使用不同的編碼方案並顯示不同的行為。對於標準的 Base64 編碼方案,請使用 Base64.Default
實例。
對於「URL 和檔案名安全」(URL and Filename safe) 編碼方案,請使用 Base64.UrlSafe
實例。
對於 MIME 編碼方案,請使用 Base64.Mime
實例。當您使用 Base64.Mime
實例時,所有編碼函式每 76 個字元會插入一個換行符。在解碼情況下,任何非法字元都會被跳過,並且不會拋出例外。
Base64.Default
實例是Base64
類別的伴隨物件。因此,您可以透過Base64.encode()
和Base64.decode()
呼叫其函式,而不是Base64.Default.encode()
和Base64.Default.decode()
。
val foBytes = "fo".map { it.code.toByte() }.toByteArray()
Base64.Default.encode(foBytes) // "Zm8="
// Alternatively:
// Base64.encode(foBytes)
val foobarBytes = "foobar".map { it.code.toByte() }.toByteArray()
Base64.UrlSafe.encode(foobarBytes) // "Zm9vYmFy"
Base64.Default.decode("Zm8=") // foBytes
// Alternatively:
// Base64.decode("Zm8=")
Base64.UrlSafe.decode("Zm9vYmFy") // foobarBytes
您可以使用額外的函式將位元組編碼或解碼到現有緩衝區中,以及將編碼結果附加到提供的 Appendable
型別物件。
在 Kotlin/JVM 中,我們還添加了擴充函式 encodingWith()
和 decodingWith()
,以使您能夠使用輸入和輸出流執行 Base64 編碼和解碼。
Kotlin/Native 中 @Volatile 支援
@Volatile
在 Kotlin/Native 中為實驗性。 它可能隨時被移除或更改。需要選擇啟用(詳情請見下文)。 僅用於評估目的。我們非常感謝您在 YouTrack 上提供相關意見回饋。
如果您使用 @Volatile
標註 var
屬性,則支援欄位會被標記,以便對該欄位的任何讀取或寫入都是原子性的,並且寫入始終對其他執行緒可見。
在 1.8.20 之前,kotlin.jvm.Volatile
標註在通用標準函式庫中可用。然而,此標註僅在 JVM 中有效。如果您在 Kotlin/Native 中使用它,它將被忽略,這可能導致錯誤。
在 1.8.20 中,我們引入了一個通用標註 kotlin.concurrent.Volatile
,您可以在 JVM 和 Kotlin/Native 中使用它。
如何啟用
若要試用此功能,請使用 @OptIn(ExperimentalStdlibApi)
選擇啟用,並啟用 -language-version 1.9
編譯器選項。在 Gradle 專案中,您可以透過將以下內容添加到 build.gradle(.kts)
檔案中來實現:
tasks
.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask<*>>()
.configureEach {
compilerOptions
.languageVersion
.set(
org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9
)
}
tasks
.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask.class)
.configureEach {
compilerOptions.languageVersion =
org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9
}
Kotlin/Native 中使用正規表達式時堆疊溢位錯誤修復
在 Kotlin 的先前版本中,如果您的正規表達式輸入包含大量字元,即使正規表達式模式非常簡單,也可能發生崩潰。在 1.8.20 中,此問題已解決。有關更多資訊,請參閱 KT-46211。
序列化更新
Kotlin 1.8.20 提供了對 Kotlin K2 編譯器的 Alpha 支援並禁止透過伴隨物件進行序列化器自訂。
Kotlin K2 編譯器序列化器外掛程式原型
序列化編譯器外掛程式對 K2 的支援處於 Alpha 階段。若要使用它, 請啟用 Kotlin K2 編譯器。
從 1.8.20 開始,序列化編譯器外掛程式與 Kotlin K2 編譯器協同工作。請試用並與我們分享您的意見回饋!
禁止透過伴隨物件隱式自訂序列化器
目前,可以透過 @Serializable
標註將類別宣告為可序列化,同時在伴隨物件上透過 @Serializer
標註宣告自訂序列化器。
例如:
import kotlinx.serialization.*
@Serializable
class Foo(val a: Int) {
@Serializer(Foo::class)
companion object {
// Custom implementation of KSerializer<Foo>
}
}
在這種情況下,從 @Serializable
標註中不清楚使用了哪個序列化器。實際上,Foo
類別具有一個自訂序列化器。
為防止此類混淆,在 Kotlin 1.8.20 中,我們針對檢測到此情境時引入了編譯器警告。該警告包含解決此問題的可能遷移路徑。
如果您在程式碼中使用此類建構,我們建議您更新為以下形式:
import kotlinx.serialization.*
@Serializable(Foo.Companion::class)
class Foo(val a: Int) {
// Doesn't matter if you use @Serializer(Foo::class) or not
companion object: KSerializer<Foo> {
// Custom implementation of KSerializer<Foo>
}
}
透過這種方法,很清楚 Foo
類別使用伴隨物件中宣告的自訂序列化器。有關更多資訊,請參閱我們的 YouTrack 票證。
在 Kotlin 2.0 中,我們計劃將此編譯警告升級為編譯錯誤。我們建議 如果您看到此警告,請遷移您的程式碼。
文件更新
Kotlin 文件已獲得一些值得注意的變更:
- Spring Boot 和 Kotlin 入門 – 建立一個帶有資料庫的簡單應用程式,並了解更多關於 Spring Boot 和 Kotlin 的功能。
- 作用域函式 – 學習如何使用標準函式庫中有用的作用域函式來簡化您的程式碼。
- CocoaPods 整合 – 設定與 CocoaPods 協同工作的環境。
安裝 Kotlin 1.8.20
檢查 IDE 版本
IntelliJ IDEA 2022.2 和 2022.3 會自動建議將 Kotlin 外掛程式更新到版本 1.8.20。IntelliJ IDEA 2023.1 內建 Kotlin 外掛程式 1.8.20。
Android Studio Flamingo (222) 和 Giraffe (223) 將在下一個版本中支援 Kotlin 1.8.20。
新的命令列編譯器可在 GitHub 發布頁面 下載。
配置 Gradle 設定
為了正確下載 Kotlin 構件和依賴項,請更新您的 settings.gradle(.kts)
檔案以使用 Maven Central 儲存庫:
pluginManagement {
repositories {
mavenCentral()
gradlePluginPortal()
}
}
如果未指定儲存庫,Gradle 將使用已停用的 JCenter 儲存庫,這可能導致 Kotlin 構件相關問題。