オプトイン要件
Kotlin標準ライブラリは、特定のAPI要素を使用するための明示的な同意を要求し、与えるメカニズムを提供します。このメカニズムにより、ライブラリの作者は、APIが実験的な状態であり、将来変更される可能性がある場合など、オプトインが必要となる特定の条件についてユーザーに通知できます。
ユーザーを保護するため、コンパイラはこれらの条件について警告し、APIを使用する前にオプトインすることを要求します。
APIのオプトイン
ライブラリの作者が、自身のライブラリのAPIからの宣言をオプトインが必要としてマークしている場合、コードでそれを使用する前に明示的な同意を与える必要があります。オプトインにはいくつかの方法があります。状況に最適なアプローチを選択することをお勧めします。
ローカルでオプトイン
コードで特定のAPI要素にオプトインするには、@OptInアノテーションと実験的なAPIマーカーへの参照を使用します。たとえば、オプトインが必要なDateProviderクラスを使用するとします。
// ライブラリコード
@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
// オプトインが必要なクラス
class DateProviderコードでは、DateProviderクラスを使用する関数を宣言する前に、MyDateTimeアノテーションクラスへの参照とともに@OptInアノテーションを追加します。
// クライアントコード
@OptIn(MyDateTime::class)
// DateProviderを使用
fun getDate(): Date {
val dateProvider: DateProvider
// ...
}このアプローチでは、getDate()関数がコードの別の場所で呼び出されたり、他の開発者によって使用されたりした場合、オプトインは不要であることに注意することが重要です。
// クライアントコード
@OptIn(MyDateTime::class)
// DateProviderを使用
fun getDate(): Date {
val dateProvider: DateProvider
// ...
}
fun displayDate() {
// OK: オプトインは不要
println(getDate())
}オプトイン要件は伝播しないため、他の人が実験的なAPIを意図せずに使用する可能性があります。これを避けるには、オプトイン要件を伝播する方が安全です。
オプトイン要件の伝播
ライブラリなど、サードパーティでの使用を意図したAPIをコードで使用する場合、そのオプトイン要件を自身のAPIにも伝播させることができます。これを行うには、ライブラリで使用されているものと同じオプトイン要件アノテーションで宣言をマークします。
たとえば、DateProviderクラスを使用する関数を宣言する前に、@MyDateTimeアノテーションを追加します。
// クライアントコード
@MyDateTime
fun getDate(): Date {
// OK: この関数もオプトインが必要です
val dateProvider: DateProvider
// ...
}
fun displayDate() {
println(getDate())
// エラー: getDate()にはオプトインが必要
}この例からわかるように、アノテーションが付加された関数は@MyDateTime APIの一部であるかのように見えます。オプトインは、getDate()関数のユーザーにオプトイン要件を伝播します。
API要素のシグネチャにオプトインが必要な型が含まれる場合、シグネチャ自体もオプトインを要求する必要があります。そうでない場合、API要素がオプトインを要求しないが、そのシグネチャにオプトインを要求する型が含まれる場合、それを使用するとエラーが発生します。
// クライアントコード
@MyDateTime
fun getDate(dateProvider: DateProvider = DateProvider()): Date
@MyDateTime
fun displayDate() {
// OK: この関数もオプトインが必要です
println(getDate())
}同様に、シグネチャにオプトインが必要な型が含まれる宣言に@OptInを適用した場合でも、オプトイン要件は伝播します。
// クライアントコード
@OptIn(MyDateTime::class)
// シグネチャ内のDateProviderによりオプトインを伝播
fun getDate(dateProvider: DateProvider = DateProvider()): Date
fun displayDate() {
println(getDate())
// エラー: getDate()にはオプトインが必要
}オプトイン要件を伝播する際、API要素が安定してオプトイン要件がなくなった場合でも、オプトイン要件が残っている他のAPI要素は引き続き実験的なままです。たとえば、ライブラリの作者がgetDate()関数が安定したため、そのオプトイン要件を削除したとします。
// ライブラリコード
// オプトイン要件なし
fun getDate(): Date {
val dateProvider: DateProvider
// ...
}displayDate()関数をオプトインアノテーションを削除せずに使用した場合、オプトインが不要になったとしても、それは実験的なままです。
// クライアントコード
// まだ実験的!
@MyDateTime
fun displayDate() {
// 安定したライブラリ関数を使用
println(getDate())
}複数のAPIへのオプトイン
複数のAPIにオプトインするには、すべてのオプトイン要件アノテーションで宣言をマークします。例:
@ExperimentalCoroutinesApi
@FlowPreviewまたは、代わりに@OptInを使用します。
@OptIn(ExperimentalCoroutinesApi::class, FlowPreview::class)ファイルでオプトイン
ファイル内のすべての関数とクラスに対してオプトインが必要なAPIを使用するには、パッケージ指定とインポートの前に、ファイルレベルのアノテーション@file:OptInをファイルの先頭に追加します。
// クライアントコード
@file:OptIn(MyDateTime::class)モジュールでオプトイン
-opt-inコンパイラオプションはKotlin 1.6.0以降で利用可能です。それ以前のKotlinバージョンでは、-Xopt-inを使用してください。
オプトインが必要なAPIのすべての使用箇所にアノテーションを付けたくない場合、モジュール全体でそれらの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メソッドを使用します。
kotlin {
compilerOptions {
optIn.add("org.mylibrary.OptInAnnotation")
}
}kotlin {
compilerOptions {
optIn.add('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インターフェースを使用するとします。
// ライブラリコード
@RequiresOptIn(
level = RequiresOptIn.Level.WARNING,
message = "Interfaces in this library are experimental"
)
annotation class UnstableApi()
@SubclassOptInRequired(UnstableApi::class)
// 拡張にオプトインが必要なインターフェース
interface CoreLibraryApiコードでは、CoreLibraryApiインターフェースを継承する新しいインターフェースを作成する前に、UnstableApiアノテーションクラスへの参照とともに@SubclassOptInRequiredアノテーションを追加します。
// クライアントコード
@SubclassOptInRequired(UnstableApi::class)
interface SomeImplementation : CoreLibraryApiクラスに@SubclassOptInRequiredアノテーションを使用する場合、オプトイン要件はインナークラスまたはネストされたクラスには伝播しないことに注意してください。
// ライブラリコード
@RequiresOptIn
annotation class ExperimentalFeature
@SubclassOptInRequired(ExperimentalFeature::class)
open class FileSystem {
open class File
}
// クライアントコード
// オプトインが必要
class NetworkFileSystem : FileSystem()
// ネストされたクラス
// オプトインは不要
class TextFile : FileSystem.File()あるいは、@OptInアノテーションを使用してオプトインすることもできます。また、実験的なマーカーアノテーションを使用して、コード内のそのクラスのすべての使用箇所に要件をさらに伝播させることもできます。
// クライアントコード
// @OptInアノテーションを使用
@OptInRequired(UnstableApi::class)
interface SomeImplementation : CoreLibraryApi
// アノテーションクラスを参照するアノテーションを使用
// オプトイン要件をさらに伝播
@UnstableApi
interface SomeImplementation : CoreLibraryApiAPIの使用にオプトインを要求
ライブラリのユーザーがAPIを使用する前にオプトインすることを要求できます。さらに、オプトイン要件を削除するまで、APIを使用するための特別な条件についてユーザーに通知することもできます。
オプトイン要件アノテーションの作成
モジュールのAPIの使用にオプトインを要求するには、オプトイン要件アノテーションとして使用するアノテーションクラスを作成します。このクラスは@RequiresOptInでアノテーションを付ける必要があります。
@RequiresOptIn
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
annotation class MyDateTimeオプトイン要件アノテーションはいくつかの要件を満たす必要があります。これらは次のものを持つ必要があります。
オプトイン要件は、2つの重要度レベルのいずれかを持つことができます。
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がある場合です。
- 進行中の進化のため実装が不安定な場合。たとえば、デフォルト実装のない新しい抽象関数を追加する予定のインターフェース群がある場合など。
- 実装が繊細または脆弱な場合。たとえば、協調して動作する必要がある個別の関数など。
- 外部実装に対して後方互換性のない方法で将来的に契約が弱められる可能性がある場合。たとえば、以前は
null値を考慮していなかったコードで、入力パラメータTをnullableバージョンT?に変更する場合など。
そのような場合、ユーザーがAPIをさらに拡張する前に、そのAPIにオプトインすることを要求できます。ユーザーは、APIを継承するか、抽象関数を実装することによってAPIを拡張できます。@SubclassOptInRequiredアノテーションを使用することで、オープンクラスや抽象クラス、非関数型インターフェースに対するこのオプトイン要件を強制できます。
API要素にオプトイン要件を追加するには、アノテーションクラスへの参照とともに@SubclassOptInRequiredアノテーションを使用します。
@RequiresOptIn(
level = RequiresOptIn.Level.WARNING,
message = "Interfaces in this library are experimental"
)
annotation class UnstableApi()
@SubclassOptInRequired(UnstableApi::class)
// 拡張にオプトインが必要なインターフェース
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