選擇啟用要求
Kotlin 標準函式庫提供了一種機制,用於要求並給予明確同意以使用特定的 API 元素。此機制允許函式庫作者告知使用者需要選擇啟用的特定條件,例如當 API 處於實驗性狀態並可能在未來變更時。
為了保護使用者,編譯器會針對這些條件發出警告,並要求使用者必須選擇啟用後才能使用該 API。
選擇啟用 API
如果函式庫作者將其函式庫 API 中的宣告標記為**需要選擇啟用**,您必須先給予明確同意才能在程式碼中使用它。有幾種選擇啟用的方式。我們建議選擇最適合您情況的方法。
本地選擇啟用
若要在程式碼中使用特定的 API 元素時選擇啟用,請使用 @OptIn
註解並參照實驗性 API 標記。例如,假設您想使用需要選擇啟用的 DateProvider
類別:
// Library code
@RequiresOptIn(message = "This API is experimental. It could change in the future without notice.")
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
annotation class MyDateTime
@MyDateTime
// A class requiring opt-in
class DateProvider
在您的程式碼中,在宣告使用 DateProvider
類別的函式之前,加入 @OptIn
註解並參照 MyDateTime
註解類別:
// Client code
@OptIn(MyDateTime::class)
// Uses DateProvider
fun getDate(): Date {
val dateProvider: DateProvider
// ...
}
值得注意的是,使用這種方法時,如果 getDate()
函式在您的程式碼其他地方被呼叫或被其他開發人員使用,則不需要選擇啟用:
// Client code
@OptIn(MyDateTime::class)
// Uses DateProvider
fun getDate(): Date {
val dateProvider: DateProvider
// ...
}
fun displayDate() {
// OK: No opt-in is required
println(getDate())
}
選擇啟用要求不會傳播,這表示其他人可能會在不知情的情況下使用實驗性 API。為避免這種情況,傳播選擇啟用要求會更安全。
傳播選擇啟用要求
當您在程式碼中使用供第三方使用的 API(例如在函式庫中),您也可以將其選擇啟用要求傳播到您的 API。為此,請使用函式庫所使用的相同**選擇啟用要求註解**來標記您的宣告。
例如,在宣告使用 DateProvider
類別的函式之前,加入 @MyDateTime
註解:
// Client code
@MyDateTime
fun getDate(): Date {
// OK: the function requires opt-in as well
val dateProvider: DateProvider
// ...
}
fun displayDate() {
println(getDate())
// Error: getDate() requires opt-in
}
如本範例所示,帶註解的函式看起來像是 @MyDateTime
API 的一部分。該選擇啟用將選擇啟用要求傳播給 getDate()
函式的使用者。
如果 API 元素的簽章包含需要選擇啟用的類型,則該簽章本身也必須要求選擇啟用。否則,如果 API 元素不需要選擇啟用,但其簽章包含需要選擇啟用的類型,使用它將會觸發錯誤。
// Client code
@MyDateTime
fun getDate(dateProvider: DateProvider = DateProvider()): Date
@MyDateTime
fun displayDate() {
// OK: the function requires opt-in as well
println(getDate())
}
同樣地,如果您將 @OptIn
應用於其簽章包含需要選擇啟用的類型的宣告,選擇啟用要求仍然會傳播:
// Client code
@OptIn(MyDateTime::class)
// Propagates opt-in due to DateProvider in the signature
fun getDate(dateProvider: DateProvider = DateProvider()): Date
fun displayDate() {
println(getDate())
// Error: getDate() requires opt-in
}
在傳播選擇啟用要求時,重要的是要了解,如果某個 API 元素變得穩定且不再有選擇啟用要求,任何仍帶有選擇啟用要求的其他 API 元素仍將保持實驗性。例如,假設函式庫作者移除了 getDate()
函式的選擇啟用要求,因為它現在已穩定:
// Library code
// No opt-in requirement
fun getDate(): Date {
val dateProvider: DateProvider
// ...
}
如果您在使用 displayDate()
函式時未移除選擇啟用註解,即使該選擇啟用已不再需要,它仍將保持實驗性:
// Client code
// Still experimental!
@MyDateTime
fun displayDate() {
// Uses a stable library function
println(getDate())
}
選擇啟用多個 API
若要選擇啟用多個 API,請使用所有相關的選擇啟用要求註解來標記該宣告。例如:
@ExperimentalCoroutinesApi
@FlowPreview
或者,也可以使用 @OptIn
:
@OptIn(ExperimentalCoroutinesApi::class, FlowPreview::class)
檔案級選擇啟用
若要為檔案中的所有函式和類別使用需要選擇啟用的 API,請在檔案頂部、套件規格和匯入之前,加入檔案級註解 @file:OptIn
。
// Client code
@file:OptIn(MyDateTime::class)
模組級選擇啟用
NOTE
-opt-in
編譯器選項自 Kotlin 1.6.0 起可用。對於較早的 Kotlin 版本,請使用 -Xopt-in
。
如果您不想註解每個需要選擇啟用的 API 使用處,您可以為整個模組選擇啟用它們。若要在模組中選擇啟用 API 的使用,請使用參數 -opt-in
編譯它,並指定您所使用 API 的選擇啟用要求註解的完全限定名稱:-opt-in=org.mylibrary.OptInAnnotation
。使用此參數編譯的效果,如同模組中的每個宣告都帶有註解 @OptIn(OptInAnnotation::class)
。
如果您使用 Gradle 建構您的模組,可以像這樣加入參數:
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask
// ...
tasks.named<KotlinCompilationTask<*>>("compileKotlin").configure {
compilerOptions.optIn.add("org.mylibrary.OptInAnnotation")
}
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask
// ...
tasks.named('compileKotlin', KotlinCompilationTask) {
compilerOptions {
optIn.add('org.mylibrary.OptInAnnotation')
}
}
如果您的 Gradle 模組是多平台模組,請使用 optIn
方法:
sourceSets {
all {
languageSettings.optIn("org.mylibrary.OptInAnnotation")
}
}
sourceSets {
all {
languageSettings {
optIn('org.mylibrary.OptInAnnotation')
}
}
}
對於 Maven,請使用以下方式:
<build>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<executions>...</executions>
<configuration>
<args>
<arg>-opt-in=org.mylibrary.OptInAnnotation</arg>
</args>
</configuration>
</plugin>
</plugins>
</build>
若要在模組層級選擇啟用多個 API,請為模組中使用的每個選擇啟用要求標記加入一個所描述的參數。
選擇啟用繼承類別或介面
有時,函式庫作者提供 API,但希望要求使用者在繼承它之前明確選擇啟用。例如,函式庫 API 可能穩定供使用,但不能用於繼承,因為它在未來可能透過新的抽象函式進行擴展。函式庫作者可以透過使用 @SubclassOptInRequired
註解來標記 開放 或 抽象類別 以及 非功能性介面 來強制執行此要求。
若要選擇啟用使用此類 API 元素並在程式碼中繼承它,請使用 @SubclassOptInRequired
註解並參照該註解類別。例如,假設您想使用需要選擇啟用的 CoreLibraryApi
介面:
// Library code
@RequiresOptIn(
level = RequiresOptIn.Level.WARNING,
message = "Interfaces in this library are experimental"
)
annotation class UnstableApi()
@SubclassOptInRequired(UnstableApi::class)
// An interface requiring opt-in to extend
interface CoreLibraryApi
在您的程式碼中,在建立繼承自 CoreLibraryApi
介面的新介面之前,加入 @SubclassOptInRequired
註解並參照 UnstableApi
註解類別:
// Client code
@SubclassOptInRequired(UnstableApi::class)
interface SomeImplementation : CoreLibraryApi
請注意,當您在類別上使用 @SubclassOptInRequired
註解時,選擇啟用要求不會傳播到任何 內部或巢狀類別:
// Library code
@RequiresOptIn
annotation class ExperimentalFeature
@SubclassOptInRequired(ExperimentalFeature::class)
open class FileSystem {
open class File
}
// Client code
// Opt-in is required
class NetworkFileSystem : FileSystem()
// Nested class
// No opt-in required
class TextFile : FileSystem.File()
另外,您也可以使用 @OptIn
註解來選擇啟用。您也可以使用實驗性標記註解,將該要求進一步傳播到程式碼中對該類別的任何使用:
// Client code
// With @OptIn annotation
@OptInRequired(UnstableApi::class)
interface SomeImplementation : CoreLibraryApi
// With annotation referencing annotation class
// Propagates the opt-in requirement further
@UnstableApi
interface SomeImplementation : CoreLibraryApi
要求選擇啟用才能使用 API
您可以要求函式庫的使用者在能夠使用您的 API 之前選擇啟用。此外,您可以告知使用者使用您的 API 的任何特殊條件,直到您決定移除選擇啟用要求為止。
建立選擇啟用要求註解
若要要求選擇啟用才能使用您的模組 API,請建立一個註解類別作為選擇啟用要求註解來使用。此類別必須使用 @RequiresOptIn
註解:
@RequiresOptIn
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
annotation class MyDateTime
選擇啟用要求註解必須符合幾個要求。它們必須具備:
選擇啟用要求可以有以下兩種嚴重性等級之一:
RequiresOptIn.Level.ERROR
。選擇啟用是強制性的。否則,使用已標記 API 的程式碼將無法編譯。這是預設等級。RequiresOptIn.Level.WARNING
。選擇啟用並非強制性,但建議使用。否則,編譯器會發出警告。
若要設定所需的等級,請指定 @RequiresOptIn
註解的 level
參數。
此外,您可以向 API 使用者提供 message
。編譯器會向嘗試在未選擇啟用的情況下使用 API 的使用者顯示此訊息:
@RequiresOptIn(level = RequiresOptIn.Level.WARNING, message = "This API is experimental. It can be incompatibly changed in the future.")
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
annotation class ExperimentalDateTime
如果您發布多個需要選擇啟用的獨立功能,請為每個功能宣告一個註解。這使得您的 API 對客戶來說更安全,因為他們只能使用其明確接受的功能。這也表示您可以獨立地從功能中移除選擇啟用要求,使您的 API 更易於維護。
標記 API 元素
若要要求選擇啟用才能使用 API 元素,請使用選擇啟用要求註解標記其宣告:
@MyDateTime
class DateProvider
@MyDateTime
fun getTime(): Time {}
請注意,對於某些語言元素,選擇啟用要求註解不適用:
- 您不能註解屬性的支援欄位或讀取器,只能註解屬性本身。
- 您不能註解局部變數或值參數。
要求選擇啟用才能繼承 API
有時,您可能希望對 API 的哪些特定部分可以被使用和繼承進行更精細的控制。例如,當您有一些穩定可用的 API,但:
- 實作不穩定,由於持續演進,例如當您有一系列介面,預期在未來會加入沒有預設實作的新抽象函式。
- 實作上微妙或脆弱,例如需要以協調一致的方式運作的個別函式。
- 合約可能在未來被削弱,以向後不相容的方式影響外部實作,例如將輸入參數
T
變更為可為空版本T?
,而該程式碼之前並未考慮null
值。
在這種情況下,您可以要求使用者在進一步繼承您的 API 之前選擇啟用。使用者可以透過繼承 API 或實作抽象函式來擴展您的 API。透過使用 @SubclassOptInRequired
註解,您可以強制執行此選擇啟用要求,適用於 開放 或 抽象類別 以及 非功能性介面。
若要將選擇啟用要求加入 API 元素,請使用 @SubclassOptInRequired
註解並參照該註解類別:
@RequiresOptIn(
level = RequiresOptIn.Level.WARNING,
message = "Interfaces in this library are experimental"
)
annotation class UnstableApi()
@SubclassOptInRequired(UnstableApi::class)
// An interface requiring opt-in to extend
interface CoreLibraryApi
請注意,當您使用 @SubclassOptInRequired
註解來要求選擇啟用時,該要求不會傳播到任何 內部或巢狀類別。
若要查看如何在 API 中使用 @SubclassOptInRequired
註解的實際範例,請查看 kotlinx.coroutines
函式庫中的 SharedFlow
介面。
預穩定 API 的選擇啟用要求
如果您對尚未穩定的功能使用選擇啟用要求,請仔細處理 API 的階段性發布,以避免破壞客戶端程式碼。
一旦您的預穩定 API 畢業並以穩定狀態發布,請從您的宣告中移除選擇啟用要求註解。客戶端隨後可以不受限制地使用它們。然而,您應該將註解類別留在模組中,以便現有的客戶端程式碼保持相容。
為了鼓勵 API 使用者透過從其程式碼中移除任何註解並重新編譯來更新其模組,請將這些註解標記為 @Deprecated
並在棄用訊息中提供解釋。
@Deprecated("This opt-in requirement is not used anymore. Remove its usages from your code.")
@RequiresOptIn
annotation class ExperimentalDateTime