メインコンテンツまでスキップ

Kotlin 1.6.0 の新機能

公開日: 2021年11月16日

Kotlin 1.6.0では、新しい言語機能、既存の機能の最適化と改善、そしてKotlin標準ライブラリへの多くの改善が導入されています。

変更点の概要は、リリースブログ記事をご覧ください。

言語

Kotlin 1.6.0では、以前の1.5.30リリースでプレビューとして導入されたいくつかの言語機能が安定化されました。

また、さまざまな型推論の改善や、クラスの型パラメータに対するannotationのサポートも含まれています。

enum、sealed、およびBooleanのsubjectに対する網羅的なwhenステートメントの安定化

網羅的な whenステートメントは、そのsubjectのすべての可能な型または値に対するbranch、またはいくつかの型とelse branchを含んでいます。これはすべての可能なケースをカバーし、コードをより安全にします。

when式の動作と一貫性を持たせるために、まもなく網羅的でないwhenステートメントを禁止する予定です。 スムーズな移行を確実にするために、Kotlin 1.6.0では、enum、sealed、またはBooleanのsubjectを持つ網羅的でないwhenステートメントに関する警告が表示されます。 これらの警告は、将来のリリースでエラーになります。

sealed class Contact {
data class PhoneCall(val number: String) : Contact()
data class TextMessage(val number: String) : Contact()
}

fun Contact.messageCost(): Int =
when(this) { // Error: 'when' expression must be exhaustive
is Contact.PhoneCall `->` 42
}

fun sendMessage(contact: Contact, message: String) {
// Starting with 1.6.0

// Warning: Non exhaustive 'when' statements on Boolean will be
// prohibited in 1.7, add 'false' branch or 'else' branch instead
when(message.isEmpty()) {
true `->` return
}
// Warning: Non exhaustive 'when' statements on sealed class/interface will be
// prohibited in 1.7, add 'is TextMessage' branch or 'else' branch instead
when(contact) {
is Contact.PhoneCall `->` TODO()
}
}

変更点とその影響の詳細な説明については、こちらのYouTrackチケットをご覧ください。

supertypeとしてのsuspend関数の安定化

Kotlin 1.6.0では、suspend関数の型の実装が安定化されました。 プレビューは1.5.30で利用可能でした。

この機能は、Kotlinコルーチンを使用し、suspend関数の型を受け入れるAPIを設計する際に役立ちます。 suspend関数の型を実装する個別のクラスに必要な動作を記述することで、コードを効率化できます。

class MyClickAction : suspend () `->` Unit {
override suspend fun invoke() { TODO() }
}

fun launchOnClick(action: suspend () `->` Unit) {}

このクラスのインスタンスは、以前はラムダ式とsuspend関数の参照のみが許可されていた場所で使用できます: launchOnClick(MyClickAction())

現在、実装の詳細に起因する2つの制限があります。

  • 通常の関数型とsuspend関数型をsupertypeのリストに混在させることはできません。
  • 複数のsuspend関数型supertypeを使用することはできません。

suspend変換の安定化

Kotlin 1.6.0では、通常の関数型からsuspend関数型への安定化された変換が導入されました。 1.4.0以降、この機能は関数リテラルとcallableな参照をサポートしていました。 1.6.0では、すべての形式の式で機能します。呼び出し引数として、suspend関数が期待される場所に、適切な通常の関数型の式を渡せるようになりました。 コンパイラは暗黙的な変換を自動的に実行します。

fun getSuspending(suspending: suspend () `->` Unit) {}

fun suspending() {}

fun test(regular: () `->` Unit) {
getSuspending { } // OK
getSuspending(::suspending) // OK
getSuspending(regular) // OK
}

annotationクラスのインスタンス化の安定化

Kotlin 1.5.30では、JVMプラットフォームでのannotationクラスのインスタンス化に対する実験的なサポートが導入されました。 1.6.0では、この機能はKotlin/JVMとKotlin/JSの両方でデフォルトで使用できます。

annotationクラスのインスタンス化の詳細については、こちらのKEEPをご覧ください。

再帰的なジェネリック型に対する型推論の改善

Kotlin 1.5.30では、再帰的なジェネリック型に対する型推論の改善が導入されました。これにより、対応する型パラメータの上限のみに基づいて型引数を推論できるようになりました。 この改善はコンパイラオプションで使用可能でした。バージョン1.6.0以降では、デフォルトで有効になっています。

// Before 1.5.30
val containerA = PostgreSQLContainer<Nothing>(DockerImageName.parse("postgres:13-alpine")).apply {
withDatabaseName("db")
withUsername("user")
withPassword("password")
withInitScript("sql/schema.sql")
}

// With compiler option in 1.5.30 or by default starting with 1.6.0
val containerB = PostgreSQLContainer(DockerImageName.parse("postgres:13-alpine"))
.withDatabaseName("db")
.withUsername("user")
.withPassword("password")
.withInitScript("sql/schema.sql")

builder推論の変更

builder推論は、ジェネリックなbuilder関数を呼び出す際に役立つ型推論の一種です。これは、ラムダ引数内の呼び出しからの型情報を使用して、呼び出しの型引数を推論できます。

完全に安定したbuilder推論に近づくために、いくつかの変更を加えています。1.6.0以降:

  • 1.5.30で導入されたコンパイラオプション-Xunrestricted-builder-inferenceを指定せずに、builderラムダ内でまだ推論されていない型のインスタンスを返す呼び出しを行うことができます。

  • -Xenable-builder-inferenceを使用すると、@BuilderInference annotationを適用せずに、独自のbuilderを作成できます。

    これらのbuilderのクライアントは、同じ-Xenable-builder-inferenceコンパイラオプションを指定する必要があります。

  • -Xenable-builder-inferenceを使用すると、通常の型推論で型に関する十分な情報を取得できない場合、builder推論が自動的にアクティブになります。

カスタムジェネリックbuilderの作成方法をご覧ください。

クラスの型パラメータに対するannotationのサポート

クラスの型パラメータに対するannotationのサポートは次のようになります。

@Target(AnnotationTarget.TYPE_PARAMETER)
annotation class BoxContent

class Box<@BoxContent T> {}

すべての型パラメータに対するannotationはJVM bytecodeに出力されるため、annotationプロセッサはそれらを使用できます。

動機となるユースケースについては、こちらのYouTrackチケットをお読みください。

annotationの詳細をご覧ください。

より長い期間、以前のAPIバージョンをサポート

Kotlin 1.6.0以降では、現在の安定版に加えて、以前のAPIバージョンを2つではなく3つサポートします。現在、バージョン1.3、1.4、1.5、および1.6をサポートしています。

Kotlin/JVM

Kotlin/JVMの場合、1.6.0以降、コンパイラはJVM 17に対応するbytecodeバージョンでクラスを生成できます。新しい言語バージョンには、最適化されたdelegated propertiesとrepeatable annotationsも含まれています。

1.8 JVM targetに対するruntime retentionを持つrepeatable annotations

Java 8では、repeatable annotationsが導入されました。これは、1つのコード要素に複数回適用できます。 この機能には、Javaコードに2つの宣言が必要です。@java.lang.annotation.Repeatableでマークされたrepeatable annotation自体と、その値を保持するためのcontaining annotationです。

Kotlinにもrepeatable annotationsがありますが、repeatableにするためにはannotation宣言に@kotlin.annotation.Repeatableが存在するだけで済みます。 1.6.0より前では、この機能はSOURCE retentionのみをサポートし、Javaのrepeatable annotationsとは互換性がありませんでした。 Kotlin 1.6.0では、これらの制限が取り除かれました。@kotlin.annotation.Repeatableは、任意のretentionを受け入れ、KotlinとJavaの両方でannotationをrepeatableにします。 Javaのrepeatable annotationsもKotlin側からサポートされるようになりました。

containing annotationを宣言できますが、必須ではありません。例:

  • annotation @Tag@kotlin.annotation.Repeatableでマークされている場合、Kotlinコンパイラは@Tag.Containerという名前でcontaining annotationクラスを自動的に生成します。

    @Repeatable 
    annotation class Tag(val name: String)

    // The compiler generates @Tag.Container containing annotation
  • containing annotationにカスタム名を設定するには、@kotlin.jvm.JvmRepeatable meta-annotationを適用し、明示的に宣言されたcontaining annotationクラスを引数として渡します。

    @JvmRepeatable(Tags::class)
    annotation class Tag(val name: String)

    annotation class Tags(val value: Array<Tag>)

Kotlinリフレクションは、新しい関数KAnnotatedElement.findAnnotations()を介して、KotlinとJavaの両方のrepeatable annotationsをサポートします。

Kotlinのrepeatable annotationsの詳細については、こちらのKEEPをご覧ください。

指定されたKPropertyインスタンスでget/setを呼び出すdelegated propertiesを最適化

$delegateフィールドを省略し、参照されるpropertyへの即時アクセスを生成することで、生成されたJVM bytecodeを最適化しました。

たとえば、次のコードでは

class Box<T> {
private var impl: T = ...

var content: T by ::impl
}

Kotlinはcontent$delegateフィールドを生成しなくなりました。 content変数のproperty accessorは、delegated propertyのgetValue/setValueオペレーターをスキップし、KProperty型のproperty参照オブジェクトの必要性を回避して、impl変数を直接呼び出します。

実装してくれたGoogleの同僚に感謝します!

delegated propertiesの詳細をご覧ください。

Kotlin/Native

Kotlin/Nativeは、複数の改善とコンポーネントの更新を受けています。そのうちのいくつかはプレビュー状態です。

新しいメモリマネージャのプレビュー

注記

新しいKotlin/Nativeメモリマネージャは実験的です。 いつでも削除または変更される可能性があります。オプトインが必要です(詳細は下記参照)。評価目的でのみ使用してください。 YouTrackでフィードバックをお待ちしております。

Kotlin 1.6.0では、新しいKotlin/Nativeメモリマネージャの開発プレビューを試すことができます。 これにより、JVMとNativeプラットフォームの違いを解消し、マルチプラットフォームプロジェクトで一貫した開発者エクスペリエンスを提供することに近づきます。

注目すべき変更点の1つは、Kotlin/JVMのように、トップレベルのpropertiesの遅延初期化です。トップレベルのpropertyは、同じファイルのトップレベルのpropertyまたは関数が最初にアクセスされたときに初期化されます。 このモードには、グローバルな手続き間最適化(リリースバイナリでのみ有効)も含まれており、冗長な初期化チェックが削除されます。

最近、新しいメモリマネージャに関するブログ記事を公開しました。 新しいメモリマネージャの現在の状態について学び、いくつかのデモプロジェクトを見つけたり、移行手順に直接ジャンプして自分で試してみてください。 新しいメモリマネージャがプロジェクトでどのように機能するかを確認し、YouTrackのissue trackerでフィードバックを共有してください。

Xcode 13のサポート

Kotlin/Native 1.6.0は、最新バージョンのXcodeであるXcode 13をサポートしています。Xcodeを更新して、Appleオペレーティングシステム用のKotlinプロジェクトの作業を続けることができます。

Xcode 13で追加された新しいライブラリは、Kotlin 1.6.0では使用できませんが、今後のバージョンでサポートを追加する予定です。

任意のホストでのWindows targetのコンパイル

1.6.0以降では、Windows target mingwX64およびmingwX86をコンパイルするためにWindowsホストは必要ありません。これらは、Kotlin/Nativeをサポートする任意のホストでコンパイルできます。

LLVMとリンカの更新

Kotlin/Nativeが内部で使用するLLVM依存関係をリワークしました。これにより、次のようなさまざまな利点があります。

  • LLVMバージョンを11.1.0に更新しました。
  • 依存関係のサイズが減少しました。たとえば、macOSでは、以前のバージョンの1200 MBではなく、約300 MBになりました。
  • 最新のLinuxディストリビューションでは利用できないncurses5ライブラリへの依存関係を除外しました

LLVMの更新に加えて、Kotlin/Nativeは、MingGW targetにLLDリンカ(LLVMプロジェクトのリンカ)を使用するようになりました。 これにより、以前に使用されていたld.bfdリンカよりもさまざまな利点があり、生成されたバイナリのランタイムパフォーマンスを向上させ、MinGW targetのコンパイラキャッシュをサポートできます。 LLDはDLLリンケージのインポートライブラリを必要とすることに注意してください。 詳細については、こちらのStack Overflowスレッドをご覧ください。

パフォーマンスの向上

Kotlin/Native 1.6.0では、次のパフォーマンスが向上しました。

  • コンパイル時間:コンパイラキャッシュは、linuxX64およびiosArm64 targetに対してデフォルトで有効になっています。 これにより、デバッグモードでのほとんどのコンパイルが高速化されます(最初のコンパイルを除く)。測定では、テストプロジェクトで約200%の速度向上が示されました。 コンパイラキャッシュは、追加のGradle propertyを使用して、Kotlin 1.5.0からこれらのtargetで使用できるようになりました。これでそれらを削除できます。
  • ランタイム:生成されたLLVMコードの最適化により、forループで配列を反復処理することが最大12%高速になりました。

JVMおよびJS IRバックエンドとの統合されたコンパイラプラグインABI

注記

Kotlin/Nativeで共通IRコンパイラプラグインABIを使用するオプションは実験的です。 いつでも削除または変更される可能性があります。オプトインが必要です(詳細は下記参照)。評価目的でのみ使用してください。 YouTrackでフィードバックをお待ちしております。

以前のバージョンでは、コンパイラプラグインの作成者は、ABIの違いにより、Kotlin/Native用の個別のアーティファクトを提供する必要がありました。

1.6.0以降、Kotlin Multiplatform Gradleプラグインは、埋め込み可能なコンパイラjar(JVMおよびJS IRバックエンドで使用されるもの)をKotlin/Nativeに使用できます。 これは、コンパイラプラグインの開発エクスペリエンスの統合に向けた一歩です。Nativeおよびその他のサポートされているプラットフォームで同じコンパイラプラグインアーティファクトを使用できるようになったためです。

これはそのようなサポートのプレビューバージョンであり、オプトインが必要です。 Kotlin/Nativeにジェネリックコンパイラプラグインアーティファクトの使用を開始するには、次の行をgradle.propertiesに追加します:kotlin.native.useEmbeddableCompilerJar=true

今後、Kotlin/Nativeで埋め込み可能なコンパイラjarをデフォルトで使用する予定ですので、プレビューがどのように機能するかをお知らせいただくことが重要です。

コンパイラプラグインの作成者の方は、このモードを試して、プラグインで動作するかどうかを確認してください。 プラグインの構造によっては、移行手順が必要になる場合があることに注意してください。移行手順については、こちらのYouTrack issueを参照し、コメントでフィードバックをお寄せください。

klibリンケージ失敗の詳細なエラーメッセージ

Kotlin/Nativeコンパイラは、klibリンケージエラーの詳細なエラーメッセージを提供するようになりました。 メッセージには、明確なエラーの説明があり、考えられる原因と修正方法に関する情報も含まれています。

例:

  • 1.5.30:

    e: java.lang.IllegalStateException: IrTypeAliasSymbol expected: Unbound public symbol for public kotlinx.coroutines/CancellationException|null[0]
    <stack trace>
  • 1.6.0:

    e: The symbol of unexpected type encountered during IR deserialization: IrClassPublicSymbolImpl, kotlinx.coroutines/CancellationException|null[0].
    IrTypeAliasSymbol is expected.

    This could happen if there are two libraries, where one library was compiled against the different version of the other library than the one currently used in the project.
    Please check that the project configuration is correct and has consistent versions of dependencies.

    The list of libraries that depend on "org.jetbrains.kotlinx:kotlinx-coroutines-core (org.jetbrains.kotlinx:kotlinx-coroutines-core-macosx64)" and may lead to conflicts:
    <list of libraries and potential version mismatches>

    Project dependencies:
    <dependencies tree>

リワークされた未処理の例外処理API

Kotlin/Nativeランタイム全体の未処理の例外の処理を統合し、カスタム実行環境(kotlinx.coroutinesなど)で使用するために、デフォルトの処理を関数processUnhandledException(throwable: Throwable)として公開しました。 この処理は、新しいメモリマネージャの場合にのみ、Worker.executeAfter()の操作からエスケープする例外にも適用されます。

APIの改善は、setUnhandledExceptionHook()によって設定されたフックにも影響しました。以前は、Kotlin/Nativeランタイムが未処理の例外でフックを呼び出した後、このようなフックはリセットされ、プログラムはすぐに終了していました。 現在、これらのフックは複数回使用でき、未処理の例外でプログラムを常に終了させたい場合は、未処理の例外フックを設定しない(setUnhandledExceptionHook())か、フックの最後にterminateWithUnhandledException()を必ず呼び出してください。 これは、例外をサードパーティのクラッシュレポートサービス(Firebase Crashlyticsなど)に送信してから、プログラムを終了するのに役立ちます。 main()からエスケープする例外と、interop境界を越える例外は、フックがterminateWithUnhandledException()を呼び出さなかった場合でも、常にプログラムを終了させます。

Kotlin/JS

Kotlin/JSコンパイラのIRバックエンドの安定化に取り組んでいます。 Kotlin/JSには、Node.jsとYarnのダウンロードを無効にするオプションがあります。

事前インストールされたNode.jsとYarnを使用するオプション

Kotlin/JSプロジェクトをビルドするときにNode.jsとYarnのダウンロードを無効にして、ホストにすでにインストールされているインスタンスを使用できるようになりました。 これは、CIサーバーなど、インターネット接続のないサーバーでビルドする場合に役立ちます。

外部コンポーネントのダウンロードを無効にするには、次の行をbuild.gradle(.kts)に追加します。

  • Yarn:

    rootProject.plugins.withType<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin> {
    rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().download = false // or true for default behavior
    }
  • Node.js:

    rootProject.plugins.withType<org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin> {
    rootProject.the<org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension>().download = false // or true for default behavior
    }

Kotlin Gradleプラグイン

Kotlin 1.6.0では、KotlinGradleSubpluginクラスの非推奨レベルを'ERROR'に変更しました。 このクラスは、コンパイラプラグインの作成に使用されていました。今後のリリースでは、このクラスを削除します。代わりに、クラスKotlinCompilerPluginSupportPluginを使用してください。

kotlin.useFallbackCompilerSearchビルドオプションとnoReflectおよびincludeRuntimeコンパイラオプションを削除しました。 useIRコンパイラオプションは非表示になっており、今後のリリースで削除されます。

Kotlin Gradleプラグインで現在サポートされているコンパイラオプションの詳細をご覧ください。

標準ライブラリ

標準ライブラリの新しい1.6.0バージョンでは、実験的な機能が安定化され、新しい機能が導入され、プラットフォーム全体での動作が統一されます。

新しいreadline関数

Kotlin 1.6.0は、標準入力を処理するための新しい関数を提供します:readln()readlnOrNull()

現在のところ、新しい関数はJVMおよびNative targetプラットフォームでのみ使用できます。

以前のバージョン1.6.0の代替使用法
readLine()!!readln()stdinから行を読み取り、それを返します。EOFに達した場合はRuntimeExceptionをスローします。
readLine()readlnOrNull()stdinから行を読み取り、それを返します。EOFに達した場合はnullを返します。

行を読み取るときに!!を使用する必要がなくなることで、初心者のエクスペリエンスが向上し、Kotlinの教育が簡素化されると考えています。 読み取り行の操作名をprintln()の対応するものと一致させるために、新しい関数の名前を'ln'に短縮することにしました。

println("What is your nickname?")
val nickname = readln()
println("Hello, $nickname!")
fun main() {

var sum = 0
while (true) {
val nextLine = readlnOrNull().takeUnless {
it.isNullOrEmpty()
} ?: break
sum += nextLine.toInt()
}
println(sum)

}

既存のreadLine()関数は、IDEコード補完でreadln()およびreadlnOrNull()よりも低い優先度になります。 IDEインスペクションは、従来のreadLine()の代わりに新しい関数を使用することを推奨します。

今後のリリースでは、readLine()関数を徐々に非推奨にする予定です。

Stable typeOf()

バージョン1.6.0では、安定化されたtypeOf()関数が導入され、主要なロードマップアイテムの1つが完了しました。

1.3.40以降typeOf()はJVMプラットフォームで実験的なAPIとして利用可能でした。 これで、任意のKotlinプラットフォームで使用して、コンパイラが推論できる任意のKotlin型のKType表現を取得できます。

inline fun <reified T> renderType(): String {
val type = typeOf<T>()
return type.toString()
}

fun main() {
val fromExplicitType = typeOf<Int>()
val fromReifiedType = renderType<List<Int>>()
}

Stable collection builders

Kotlin 1.6.0では、collection builder関数が安定化されました。collection builderによって返されるコレクションは、読み取り専用の状態でシリアライズ可能になりました。

annotationをオプトインせずに、buildMap()buildList()、およびbuildSet()を使用できるようになりました。

fun main() {

val x = listOf('b', 'c')
val y = buildList {
add('a')
addAll(x)
add('d')
}
println(y) // [a, b, c, d]

}

Stable Duration API

さまざまな時間単位で期間を表すDurationクラスが安定化されました。1.6.0では、Duration APIは次の変更を受けました。

  • 期間を日、時間、分、秒、およびナノ秒に分解するtoComponents()関数の最初のコンポーネントは、Int型ではなくLong型になりました。 以前は、値がIntの範囲に収まらない場合、その範囲に強制的に変換されていました。Long型を使用すると、Intに収まらない値を切り捨てることなく、期間範囲内の任意の値に分解できます。

  • DurationUnit enumはスタンドアロンになり、JVMのjava.util.concurrent.TimeUnitの型エイリアスではなくなりました。 typealias DurationUnit = TimeUnitが役立つ可能性のある説得力のあるケースは見つかりませんでした。また、型エイリアスを介してTimeUnit APIを公開すると、DurationUnitユーザーを混乱させる可能性があります。

  • コミュニティからのフィードバックに応えて、Int.secondsのような拡張propertiesを復活させます。ただし、その適用性を制限したいので、Durationクラスのコンパニオンに配置しました。 IDEは引き続き補完で拡張機能を提案し、コンパニオンからのインポートを自動的に挿入できますが、将来的には、この動作をDuration型が期待される場合に制限する予定です。

    import kotlin.time.Duration.Companion.seconds

    fun main() {

    val duration = 10000
    println("There are ${duration.seconds.inWholeMinutes} minutes in $duration seconds")
    // There are 166 minutes in 10000 seconds

    }

    Duration.seconds(Int)などの以前に導入されたコンパニオン関数と、Int.secondsのような非推奨のトップレベル拡張機能を、Duration.Companionの新しい拡張機能に置き換えることをお勧めします。

    このような置換は、古いトップレベル拡張機能と新しいコンパニオン拡張機能の間であいまいさを引き起こす可能性があります。 自動移行を行う前に、kotlin.timeパッケージのワイルドカードインポート – import kotlin.time.* – を必ず使用してください。

Regexをシーケンスに分割

Regex.splitToSequence(CharSequence)関数とCharSequence.splitToSequence(Regex)関数が安定化されました。 これらは、指定された正規表現の一致箇所で文字列を分割しますが、この結果に対するすべての操作が遅延実行されるように、結果をSequenceとして返します。

fun main() {

val colorsText = "green, red, brown&blue, orange, pink&green"
val regex = "[,\\s]+".toRegex()
val mixedColor = regex.splitToSequence(colorsText)
// or
// val mixedColor = colorsText.splitToSequence(regex)
.onEach { println(it) }
.firstOrNull { it.contains('&') }
println(mixedColor) // "brown&blue"

}

整数に対するビット回転操作

Kotlin 1.6.0では、ビット操作用のrotateLeft()関数とrotateRight()関数が安定化されました。 これらの関数は、数のバイナリ表現を指定されたビット数だけ左または右に回転させます。

fun main() {

val number: Short = 0b10001
println(number
.rotateRight(2)
.toString(radix = 2)) // 100000000000100
println(number
.rotateLeft(2)
.toString(radix = 2)) // 1000100

}

JSでのreplace()とreplaceFirst()の変更

Kotlin 1.6.0より前は、[replace()](https://kotlinlang.org/api/