Skip to content

Kotlin 1.6.20 の新機能

リリース日: 2022年4月4日

Kotlin 1.6.20 では、将来の言語機能のプレビューが公開され、マルチプラットフォームプロジェクトの階層構造がデフォルトとなり、その他のコンポーネントにも進化的な改善がもたらされます。

変更点の概要をまとめた短い動画もご覧いただけます。

言語

Kotlin 1.6.20 では、2つの新しい言語機能を試すことができます。

Kotlin/JVM のコンテキストレシーバのプロトタイプ

この機能はKotlin/JVMのみで利用可能なプロトタイプです。-Xcontext-receiversを有効にすると、 コンパイラはプレリリースバイナリを生成し、これらは製品コードでは使用できません。 コンテキストレシーバは個人プロジェクトでのみ使用してください。 フィードバックはYouTrackでお待ちしています。

Kotlin 1.6.20 では、レシーバを1つに限定する必要がなくなりました。より多くのレシーバが必要な場合は、関数、プロパティ、クラスの宣言にコンテキストレシーバを追加することで、それらをコンテキスト依存 (または コンテキスト的) にすることができます。コンテキスト宣言では、以下の処理が行われます。

  • 宣言されたすべてのコンテキストレシーバが、呼び出し元のスコープに暗黙のレシーバとして存在する必要があります。
  • 宣言されたコンテキストレシーバを、自身の本体スコープに暗黙のレシーバとして取り込みます。
kotlin
interface LoggingContext {
    val log: Logger // このコンテキストはロガーへの参照を提供します
}

context(LoggingContext)
fun startBusinessOperation() {
    // LoggingContextは暗黙のレシーバであるため、logプロパティにアクセスできます
    log.info("Operation has started")
}

fun test(loggingContext: LoggingContext) {
    with(loggingContext) {
        // startBusinessOperation() を呼び出すには、スコープ内にLoggingContextを暗黙のレシーバとして持つ必要があります
        startBusinessOperation()
    }
}

プロジェクトでコンテキストレシーバを有効にするには、-Xcontext-receiversコンパイラオプションを使用します。 この機能とその構文の詳細な説明は、KEEPで確認できます。

この実装はプロトタイプであることに注意してください。

  • -Xcontext-receiversを有効にすると、コンパイラは製品コードでは使用できないプレリリースバイナリを生成します。
  • コンテキストレシーバのIDEサポートは現状最小限です。

この機能を個人プロジェクトで試して、このYouTrack課題で感想や体験を共有してください。 問題が発生した場合は、新しい課題を提出してください。

Definitely non-nullable types

Definitely non-nullable typesはベータ版です。ほぼ安定していますが、 将来的に移行手順が必要になる場合があります。 変更を最小限に抑えるよう最善を尽くします。

ジェネリックJavaクラスやインターフェースを拡張する際により良い相互運用性を提供するために、Kotlin 1.6.20では、新しい構文T & Anyを使用することで、利用箇所でジェネリック型パラメータを「Definitely non-nullable」としてマークできるようになりました。 この構文形式はIntersection typesの表記法に由来しており、現在は&の左側にnullableな上限を持つ型パラメータ、右側にnon-nullableなAnyを持つ型パラメータに限定されています。

kotlin
fun <T> elvisLike(x: T, y: T & Any): T & Any = x ?: y

fun main() {
    // OK
    elvisLike<String>("", "").length
    // エラー: 'null' は非null型 の値にはできません
    elvisLike<String>("", null).length

    // OK
    elvisLike<String?>(null, "").length
    // エラー: 'null' は非null型 の値にはできません
    elvisLike<String?>(null, null).length
}

この機能を有効にするには、言語バージョンを1.7に設定してください。

kotlin
kotlin {
    sourceSets.all {
        languageSettings.apply {
            languageVersion = "1.7"
        }
    }
}
groovy
kotlin {
    sourceSets.all {
        languageSettings {
            languageVersion = '1.7'
        }
    }
}

Definitely non-nullable typesの詳細については、KEEPを参照してください。

Kotlin/JVM

Kotlin 1.6.20 で導入される機能:

インターフェースのための新しい @JvmDefaultWithCompatibility アノテーション

Kotlin 1.6.20 では、新しいアノテーション@JvmDefaultWithCompatibilityが導入されます。このアノテーションを-Xjvm-default=allコンパイラオプションと組み合わせて使用すると、任意のKotlinインターフェース内の非抽象メンバに対してJVMインターフェースのデフォルトメソッドが作成されます

もしあなたのKotlinインターフェースを-Xjvm-default=allオプションなしでコンパイルされたクライアントが使用している場合、このオプションでコンパイルされたコードとバイナリ互換性がない可能性があります。 Kotlin 1.6.20 より前では、この互換性の問題を回避するために、推奨されるアプローチ-Xjvm-default=all-compatibilityモードを使用し、この種の互換性を必要としないインターフェースには@JvmDefaultWithoutCompatibilityアノテーションも使用することでした。

このアプローチにはいくつかの欠点がありました。

  • 新しいインターフェースが追加されたときにアノテーションを追加し忘れる可能性がありました。
  • 通常、公開API以外の部分にはより多くのインターフェースがあるため、コードの多くの場所にこのアノテーションが存在することになります。

現在、-Xjvm-default=allモードを使用し、インターフェースに@JvmDefaultWithCompatibilityアノテーションを付けることができます。 これにより、公開APIのすべてのインターフェースに一度だけこのアノテーションを追加でき、新しい非公開コードにはアノテーションを使用する必要がなくなります。

この新しいアノテーションに関するフィードバックは、このYouTrackチケットにご記入ください。

-Xjvm-default モードにおける互換性の変更

Kotlin 1.6.20 では、デフォルトモード (-Xjvm-default=disable コンパイラオプション) のモジュールを、-Xjvm-default=all または -Xjvm-default=all-compatibility モードでコンパイルされたモジュールに対してコンパイルするオプションが追加されました。 従来通り、すべてのモジュールが-Xjvm-default=allまたは-Xjvm-default=all-compatibilityモードである場合もコンパイルは成功します。 フィードバックはこのYouTrack課題にご記入いただけます。

Kotlin 1.6.20 では、コンパイラオプション-Xjvm-defaultcompatibilityモードとenableモードが非推奨になりました。 他のモードの説明にも互換性に関する変更がありますが、全体的なロジックは同じです。 更新された説明を確認できます。

Javaとの相互運用におけるデフォルトメソッドの詳細については、相互運用ドキュメントこのブログ記事を参照してください。

JVMバックエンドにおける単一モジュールの並列コンパイルのサポート

JVMバックエンドにおける単一モジュールの並列コンパイルのサポートは実験的です。 いつでも削除または変更される可能性があります。オプトインが必要です(詳細は下記参照)。評価目的でのみ使用してください。 フィードバックはYouTrackでお待ちしています。

新しいJVM IRバックエンドのコンパイル時間を改善するための作業を継続しています。 Kotlin 1.6.20 では、モジュール内のすべてのファイルを並列でコンパイルする実験的なJVM IRバックエンドモードを追加しました。 並列コンパイルにより、全体のコンパイル時間を最大15%短縮できます。

実験的な並列バックエンドモードを有効にするには、コンパイラオプション-Xbackend-threadsを使用します。 このオプションには以下の引数を指定します。

  • Nは使用したいスレッド数です。CPUコア数よりも大きくしてはいけません。そうしないと、スレッド間のコンテキスト切り替えにより並列化が効果を発揮しなくなります。
  • 0は各CPUコアに独立したスレッドを使用します。

Gradleはタスクを並列で実行できますが、プロジェクト(またはプロジェクトの主要部分)がGradleの観点から見て一つの大きなタスクである場合、この種の並列化はあまり役に立ちません。 非常に大きなモノリシックモジュールがある場合は、並列コンパイルを使用してより速くコンパイルしてください。 プロジェクトが多数の小さなモジュールで構成されており、Gradleによってビルドが並列化されている場合、コンテキスト切り替えのために別の並列化レイヤーを追加すると、パフォーマンスが低下する可能性があります。

並列コンパイルにはいくつかの制約があります。

  • kaptはIRバックエンドを無効にするため、kaptとは動作しません。
  • 設計上、より多くのJVMヒープが必要です。ヒープ量はスレッド数に比例します。

関数型インターフェースのコンストラクタへの呼び出し可能な参照のサポート

関数型インターフェースのコンストラクタへの呼び出し可能な参照のサポートは実験的です。 いつでも削除または変更される可能性があります。オプトインが必要です(詳細は下記参照)。評価目的でのみ使用してください。 フィードバックはYouTrackでお待ちしています。

関数型インターフェースのコンストラクタへの呼び出し可能な参照のサポートにより、コンストラクタ関数を持つインターフェースから関数型インターフェースへの移行を、ソース互換性のある方法で実現できます。

次のコードを検討してください。

kotlin
interface Printer {
    fun print()
}

fun Printer(block: () -> Unit): Printer = object : Printer { override fun print() = block() }

関数型インターフェースのコンストラクタへの呼び出し可能な参照が有効になっている場合、このコードは単に関数型インターフェースの宣言に置き換えることができます。

kotlin
fun interface Printer {
    fun print()
}

そのコンストラクタは暗黙的に作成され、::Printer関数参照を使用するすべてのコードはコンパイルされます。例:

kotlin
documentsStorage.addPrinter(::Printer)

バイナリ互換性を維持するには、従来の関数Printer@DeprecatedアノテーションとDeprecationLevel.HIDDENでマークします。

kotlin
@Deprecated(message = "Your message about the deprecation", level = DeprecationLevel.HIDDEN)
fun Printer(...) {...}

この機能を有効にするには、コンパイラオプション-XXLanguage:+KotlinFunInterfaceConstructorReferenceを使用します。

Kotlin/Native

Kotlin/Native 1.6.20 は、新しいコンポーネントの開発が継続していることを示しています。Kotlin を他のプラットフォームで一貫したエクスペリエンスで利用できるように、さらに一歩踏み出しました。

新しいメモリマネージャーのアップデート

新しいKotlin/Nativeメモリマネージャーはアルファ版です。 将来的に互換性のない変更があり、手動での移行が必要になる場合があります。 フィードバックはYouTrackでお待ちしています。

Kotlin 1.6.20では、新しいKotlin/Nativeメモリマネージャーのアルファ版を試すことができます。 これにより、JVMとNativeプラットフォーム間の差異が解消され、マルチプラットフォームプロジェクトで一貫した開発者体験が提供されます。 たとえば、AndroidとiOSの両方で動作する新しいクロスプラットフォームモバイルアプリケーションをはるかに簡単に作成できるようになります。

新しいKotlin/Nativeメモリマネージャーは、スレッド間のオブジェクト共有の制限を解除します。 また、安全で特別な管理やアノテーションを必要としない、リークフリーな並行プログラミングプリミティブも提供します。

新しいメモリマネージャーは将来のバージョンでデフォルトになる予定ですので、今すぐ試すことをお勧めします。 新しいメモリマネージャーの詳細とデモプロジェクトについては、ブログ記事を確認するか、すぐに移行手順にジャンプして自分で試してください。

プロジェクトで新しいメモリマネージャーを使用して、その動作を確認し、YouTrackの課題トラッカーでフィードバックを共有してください。

新しいメモリマネージャーにおけるスイープフェーズの並行実装

Kotlin 1.6 で発表された新しいメモリマネージャーにすでに切り替えている場合、実行時間の大幅な改善に気づくかもしれません。当社のベンチマークでは平均で35%の改善が示されています。 1.6.20 からは、新しいメモリマネージャーでスイープフェーズの並行実装も利用できるようになりました。 これにより、パフォーマンスがさらに向上し、ガベージコレクタの一時停止時間が短縮されるはずです。

新しい Kotlin/Native メモリマネージャーでこの機能を有効にするには、以下のコンパイラオプションを渡します。

bash
-Xgc=cms

新しいメモリマネージャーのパフォーマンスに関するフィードバックは、このYouTrack課題にぜひお寄せください。

アノテーションクラスのインスタンス化

Kotlin 1.6.0 では、アノテーションクラスのインスタンス化が Kotlin/JVM および Kotlin/JS で安定版になりました。 1.6.20 バージョンでは、Kotlin/Native のサポートが提供されます。

アノテーションクラスのインスタンス化について詳しくはこちらをご覧ください。

Swift async/await との相互運用: KotlinUnit の代わりに Swift の Void を返す

Swift async/await との並行処理の相互運用性は実験的です。いつでも削除または変更される可能性があります。 評価目的でのみ使用してください。フィードバックはYouTrackでお待ちしています。

Swift 5.5 以降で利用可能な Swift の async/await との実験的な相互運用に関する作業を継続してきました。 Kotlin 1.6.20 は、Unit戻り型を持つsuspend関数の動作方法において、以前のバージョンとは異なります。

以前は、そのような関数はSwiftでKotlinUnitを返すasync関数として表現されていました。しかし、それらの適切な戻り型は、非中断関数と同様にVoidです。

既存のコードを壊さないように、コンパイラがUnitを返すsuspend関数をVoid戻り型を持つasync Swiftに変換するGradleプロパティを導入します。

none
# gradle.properties
kotlin.native.binary.unitSuspendFunctionObjCExport=proper

今後のKotlinリリースでは、この動作をデフォルトにする予定です。

libbacktrace によるより良いスタックトレース

ソース位置解決にlibbacktraceを使用することは実験的です。いつでも削除または変更される可能性があります。 評価目的でのみ使用してください。フィードバックはYouTrackでお待ちしています。

Kotlin/Native は現在、linux* (ただしlinuxMips32linuxMipsel32を除く) およびandroidNative*ターゲットのデバッグを改善するために、ファイルの位置と行番号を含む詳細なスタックトレースを生成できるようになりました。

この機能は、内部でlibbacktraceライブラリを使用しています。 次のコードで違いの例を確認してください。

kotlin
fun main() = bar()
fun bar() = baz()
inline fun baz() {
    error("")
}
  • 1.6.20 より前:
text
Uncaught Kotlin exception: kotlin.IllegalStateException:
   at 0   example.kexe        0x227190       kfun:kotlin.Throwable#<init>(kotlin.String?){} + 96
   at 1   example.kexe        0x221e4c       kfun:kotlin.Exception#<init>(kotlin.String?){} + 92
   at 2   example.kexe        0x221f4c       kfun:kotlin.RuntimeException#<init>(kotlin.String?){} + 92
   at 3   example.kexe        0x22234c       kfun:kotlin.IllegalStateException#<init>(kotlin.String?){} + 92
   at 4   example.kexe        0x25d708       kfun:#bar(){} + 104
   at 5   example.kexe        0x25d68c       kfun:#main(){} + 12
  • 1.6.20 と libbacktrace:
text
Uncaught Kotlin exception: kotlin.IllegalStateException:
   at 0   example.kexe        0x229550    kfun:kotlin.Throwable#<init>(kotlin.String?){} + 96 (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Throwable.kt:24:37)
   at 1   example.kexe        0x22420c    kfun:kotlin.Exception#<init>(kotlin.String?){} + 92 (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Exceptions.kt:23:44)
   at 2   example.kexe        0x22430c    kfun:kotlin.RuntimeException#<init>(kotlin.String?){} + 92 (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Exceptions.kt:34:44)
   at 3   example.kexe        0x22470c    kfun:kotlin.IllegalStateException#<init>(kotlin.String?){} + 92 (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Exceptions.kt:70:44)
   at 4   example.kexe        0x25fac8    kfun:#bar(){} + 104 [inlined] (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/libraries/stdlib/src/kotlin/util/Preconditions.kt:143:56)
   at 5   example.kexe        0x25fac8    kfun:#bar(){} + 104 [inlined] (/private/tmp/backtrace/src/commonMain/kotlin/app.kt:4:5)
   at 6   example.kexe        0x25fac8    kfun:#bar(){} + 104 (/private/tmp/backtrace/src/commonMain/kotlin/app.kt:2:13)
   at 7   example.kexe        0x25fa4c    kfun:#main(){} + 12 (/private/tmp/backtrace/src/commonMain/kotlin/app.kt:1:14)

Apple ターゲットでは、すでにスタックトレースにファイル位置と行番号が含まれていましたが、libbacktrace はインライン関数呼び出しについてより詳細な情報を提供します。

  • 1.6.20 より前:
text
Uncaught Kotlin exception: kotlin.IllegalStateException:
   at 0   example.kexe    0x10a85a8f8    kfun:kotlin.Throwable#<init>(kotlin.String?){} + 88 (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Throwable.kt:24:37)
   at 1   example.kexe    0x10a855846    kfun:kotlin.Exception#<init>(kotlin.String?){} + 86 (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Exceptions.kt:23:44)
   at 2   example.kexe    0x10a855936    kfun:kotlin.RuntimeException#<init>(kotlin.String?){} + 86 (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Exceptions.kt:34:44)
   at 3   example.kexe    0x10a855c86    kfun:kotlin.IllegalStateException#<init>(kotlin.String?){} + 86 (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Exceptions.kt:70:44)
   at 4   example.kexe    0x10a8489a5    kfun:#bar(){} + 117 (/private/tmp/backtrace/src/commonMain/kotlin/app.kt:2:1)
   at 5   example.kexe    0x10a84891c    kfun:#main(){} + 12 (/private/tmp/backtrace/src/commonMain/kotlin/app.kt:1:14)
...
  • 1.6.20 と libbacktrace:
text
Uncaught Kotlin exception: kotlin.IllegalStateException:
   at 0   example.kexe    0x10669bc88    kfun:kotlin.Throwable#<init>(kotlin.String?){} + 88 (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Throwable.kt:24:37)
   at 1   example.kexe    0x106696bd6    kfun:kotlin.Exception#<init>(kotlin.String?){} + 86 (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Exceptions.kt:23:44)
   at 2   example.kexe    0x106696cc6    kfun:kotlin.RuntimeException#<init>(kotlin.String?){} + 86 (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Exceptions.kt:34:44)
   at 3   example.kexe    0x106697016    kfun:kotlin.IllegalStateException#<init>(kotlin.String?){} + 86 (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Exceptions.kt:70:44)
   at 4   example.kexe    0x106689d35    kfun:#bar(){} + 117 [inlined] (/opt/buildAgent/work/c3a91df21e46e2c8/kotlin/libraries/stdlib/src/kotlin/util/Preconditions.kt:143:56)
>>  at 5   example.kexe    0x106689d35    kfun:#bar(){} + 117 [inlined] (/private/tmp/backtrace/src/commonMain/kotlin/app.kt:4:5)
   at 6   example.kexe    0x106689d35    kfun:#bar(){} + 117 (/private/tmp/backtrace/src/commonMain/kotlin/app.kt:2:13)
   at 7   example.kexe    0x106689cac    kfun:#main(){} + 12 (/private/tmp/backtrace/src/commonMain/kotlin/app.kt:1:14)
...

libbacktrace を使用してより良いスタックトレースを生成するには、gradle.propertiesに次の行を追加します。

none
# gradle.properties
kotlin.native.binary.sourceInfoType=libbacktrace

libbacktrace を使用した Kotlin/Native のデバッグがどのように機能するか、このYouTrack課題でぜひフィードバックをお寄せください。

スタンドアロンAndroid実行可能ファイルのサポート

これまで、Kotlin/NativeのAndroid Native実行可能ファイルは、実際には実行可能ファイルではなく、NativeActivityとして使用できる共有ライブラリでした。現在は、Android Nativeターゲット用の標準実行可能ファイルを生成するオプションがあります。

これを行うには、プロジェクトのbuild.gradle(.kts)部分で、androidNativeターゲットの実行可能ブロックを設定します。 以下のバイナリオプションを追加してください。

kotlin
kotlin {
    androidNativeX64("android") {
        binaries {
            executable {
                binaryOptions["androidProgramType"] = "standalone"
            }
        }
    }
}

この機能はKotlin 1.7.0でデフォルトになる予定です。 現在の動作を維持したい場合は、以下の設定を使用してください。

kotlin
binaryOptions["androidProgramType"] = "nativeActivity"

Mattia Iavarone氏の実装に感謝いたします。

パフォーマンスの改善

Kotlin/Nativeでは、コンパイルプロセスの高速化と開発体験の向上に力を入れています。

Kotlin 1.6.20 では、Kotlin が生成する LLVM IR に影響するいくつかのパフォーマンス更新とバグ修正が含まれています。 内部プロジェクトでのベンチマークによると、平均して以下のパフォーマンス向上が達成されました。

  • 実行時間 15%削減
  • リリースおよびデバッグバイナリのコードサイズ 20%削減
  • リリースバイナリのコンパイル時間 26%削減

これらの変更により、大規模な内部プロジェクトでのデバッグバイナリのコンパイル時間も10%削減されました。

これを達成するために、コンパイラによって生成される一部の合成オブジェクトに対する静的初期化を実装し、すべての関数に対するLLVM IRの構造化方法を改善し、コンパイラキャッシュを最適化しました。

cinteropモジュールのインポート時のエラー処理の改善

このリリースでは、cinteropツールを使用してObjective-Cモジュールをインポートする際(CocoaPodsのpodで典型的なケース)のエラー処理が改善されました。 これまで、Objective-Cモジュールを扱おうとした際(例えば、ヘッダーのコンパイルエラーなど)にエラーが発生すると、fatal error: could not build module $nameのような、情報が不足したエラーメッセージが表示されていました。 今回、cinteropツールのこの部分が拡張され、より詳細な説明を含むエラーメッセージが表示されるようになりました。

Xcode 13 ライブラリのサポート

Xcode 13 と共に提供されるライブラリは、このリリースから完全にサポートされます。 Kotlin コードのどこからでも自由にアクセスできます。

Kotlin Multiplatform

1.6.20では、Kotlin Multiplatformに対する以下の注目すべきアップデートが行われました。

マルチプラットフォームプロジェクトにおける階層構造のサポート

Kotlin 1.6.20 では、階層構造のサポートがデフォルトで有効になっています。 Kotlin 1.4.0 で導入されて以来、フロントエンドが大幅に改善され、IDE のインポートが安定しました。

以前は、マルチプラットフォームプロジェクトにコードを追加する方法が2つありました。1つ目は、プラットフォーム固有のソースセットに挿入する方法で、これは1つのターゲットに限定され、他のプラットフォームで再利用できませんでした。 2つ目は、Kotlinが現在サポートしているすべてのプラットフォームで共有される共通ソースセットを使用する方法です。

これで、共通ロジックやサードパーティAPIを多く再利用する、いくつかの類似するネイティブターゲット間でソースコードを共有できるようになりました。 このテクノロジーは、正しいデフォルトの依存関係を提供し、共有コードで利用可能な正確なAPIを見つけます。 これにより、複雑なビルド設定や、ネイティブターゲット間でソースセットを共有するためのIDEサポートを得るための回避策が不要になります。 また、異なるターゲット向けに意図された安全でないAPIの使用を防ぐのにも役立ちます。

このテクノロジーは、階層型プロジェクト構造により、ライブラリの作者がターゲットのサブセットに対して共通APIを持つライブラリを公開・利用できるようになるため、ライブラリの作者にとっても役立ちます。

デフォルトでは、階層型プロジェクト構造で公開されたライブラリは、階層型構造のプロジェクトとのみ互換性があります。

プロジェクト内でのコード共有の改善

階層構造のサポートがなければ、Kotlinターゲットの_すべてではなく一部_でコードを共有する直接的な方法はありません。 一般的な例の1つは、すべてのiOSターゲット間でコードを共有し、FoundationのようなiOS固有の依存関係にアクセスすることです。

階層型プロジェクト構造のサポートのおかげで、この機能をすぐに利用できるようになりました。 新しい構造では、ソースセットが階層を形成します。 与えられたソースセットがコンパイルされる各ターゲットで利用可能なプラットフォーム固有の言語機能と依存関係を使用できます。

たとえば、iOS デバイスとシミュレーター用の iosArm64iosX64 という 2 つのターゲットを持つ典型的なマルチプラットフォームプロジェクトを考えてみましょう。 Kotlin ツールは、両方のターゲットが同じ関数を持っていることを理解し、中間ソースセット iosMain からその関数にアクセスすることを許可します。

iOS hierarchy example

Kotlin ツールチェーンは、Kotlin/Native stdlib やネイティブライブラリなどの適切なデフォルトの依存関係を提供します。 さらに、Kotlin ツールは、共有コードで利用可能な正確なAPIサーフェスを見つけるために最善を尽くします。 これにより、たとえばmacOS固有の関数をWindows向けに共有されたコードで使用するなどのケースを防ぐことができます。

ライブラリ作者にとってのより多くの機会

マルチプラットフォームライブラリが公開されると、その中間ソースセットのAPIが適切に公開され、利用者が利用できるようになります。 ここでも、Kotlinツールチェーンは、JVM向けに意図されたAPIをJSコードで使用するなどの安全でない使用法に注意深く目を光らせながら、利用側のソースセットで利用可能なAPIを自動的に特定します。 ライブラリでのコード共有について詳しくはこちらをご覧ください。

設定とセットアップ

Kotlin 1.6.20 から、すべての新しいマルチプラットフォームプロジェクトで階層型プロジェクト構造が適用されます。追加の設定は不要です。

  • すでに手動でオンにしている場合は、gradle.propertiesから非推奨のオプションを削除できます。

    none
    # gradle.properties
    kotlin.mpp.enableGranularSourceSetsMetadata=true
    kotlin.native.enableDependencyPropagation=false // or 'true', depending on your previous setup
  • Kotlin 1.6.20 の場合、最高の体験を得るためにAndroid Studio 2021.1.1 (Bumblebee) 以降を使用することをお勧めします。

  • オプトアウトすることも可能です。階層構造のサポートを無効にするには、gradle.propertiesで以下のオプションを設定します。

    none
    # gradle.properties
    kotlin.mpp.hierarchicalStructureSupport=false

フィードバックをお寄せください

これはエコシステム全体にとって重要な変更です。より良いものにするために、皆様からのフィードバックをお待ちしております。

今すぐ試してみて、遭遇した問題があれば課題トラッカーに報告してください。

Kotlin CocoaPods Gradle プラグイン

CocoaPods統合を簡素化するために、Kotlin 1.6.20 では以下の機能が提供されます。

  • CocoaPods プラグインには、登録されているすべてのターゲットで XCFramework をビルドし、Podspec ファイルを生成するタスクが追加されました。これは、Xcode と直接統合したくないが、成果物をビルドしてローカルの CocoaPods リポジトリにデプロイしたい場合に役立ちます。

    XCFramework のビルドについて詳しくはこちらをご覧ください。

  • プロジェクトでCocoaPods統合を使用している場合、Gradleプロジェクト全体に必要なPodバージョンを指定するのが一般的でした。これで、さらに選択肢が増えました。

    • cocoapodsブロックでPodバージョンを直接指定する
    • 引き続きGradleプロジェクトバージョンを使用する

    これらのプロパティのいずれも設定されていない場合、エラーが発生します。

  • cocoapodsブロックでCocoaPod名を構成できるようになり、Gradleプロジェクト全体の名前を変更する必要がなくなりました。

  • CocoaPodsプラグインに新しいextraSpecAttributesプロパティが導入されました。これにより、以前はハードコードされていたlibrariesvendored_frameworksなどのPodspecファイルのプロパティを構成できます。

kotlin
kotlin {
    cocoapods {
        version = "1.0"
        name = "MyCocoaPod"
        extraSpecAttributes["social_media_url"] = 'https://twitter.com/kotlin'
        extraSpecAttributes["vendored_frameworks"] = 'CustomFramework.xcframework'
        extraSpecAttributes["libraries"] = 'xml'
    }
}

Kotlin CocoaPods Gradleプラグインの完全なDSLリファレンスを参照してください。

Kotlin/JS

Kotlin/JS の 1.6.20 における改善点は、主に IR コンパイラに影響を与えます。

IR コンパイラによる開発用バイナリのインクリメンタルコンパイル

IR コンパイラを使用した Kotlin/JS 開発をより効率的にするために、新しい インクリメンタルコンパイル モードを導入します。

このモードでcompileDevelopmentExecutableKotlinJs Gradle タスクを使用して開発用バイナリをビルドすると、コンパイラは以前のコンパイル結果をモジュールレベルでキャッシュします。 これにより、変更されていないソースファイルに対してキャッシュされたコンパイル結果が後続のコンパイル中に使用されるため、特に小さな変更の場合に、コンパイルがより迅速に完了します。 この改善は開発プロセス (編集-ビルド-デバッグサイクルの短縮) のみを対象としており、製品アーティファクトのビルドには影響しないことに注意してください。

開発用バイナリのインクリメンタルコンパイルを有効にするには、プロジェクトのgradle.propertiesに次の行を追加します。

none
# gradle.properties
kotlin.incremental.js.ir=true // false by default

当社のテストプロジェクトでは、新しいモードによりインクリメンタルコンパイルが最大30%高速化されました。ただし、このモードでのクリーンビルドは、キャッシュを作成および設定する必要があるため、遅くなりました。

Kotlin/JS プロジェクトでインクリメンタルコンパイルを使用することについてのご意見は、この YouTrack 課題までお寄せください。

IR コンパイラでトップレベルプロパティの遅延初期化がデフォルトに

Kotlin 1.4.30 では、JS IR コンパイラでトップレベルプロパティの遅延初期化のプロトタイプを提示しました。 アプリケーション起動時にすべてのプロパティを初期化する必要をなくすことで、遅延初期化は起動時間を短縮します。 当社の測定では、実際のKotlin/JSアプリケーションで約10%の高速化が示されました。

今回、このメカニズムを洗練させ、適切にテストした結果、IR コンパイラにおいてトップレベルプロパティの遅延初期化をデフォルトにすることにしました。

kotlin
// lazy initialization (遅延初期化)
val a = run {
    val result = // intensive computations (集中的な計算)
        println(result)
    result
} // run は変数が最初に利用されたときに実行されます

何らかの理由でプロパティを eager (アプリケーション起動時) に初期化する必要がある場合は、@EagerInitializationアノテーションでマークしてください。

IR コンパイラでプロジェクトモジュールのJSファイルがデフォルトで分離される

以前は、JS IR コンパイラはプロジェクトモジュールごとに個別の.jsファイルを生成する機能を提供していました。 これは、プロジェクト全体に対して1つの.jsファイルを出力するというデフォルトオプションの代替手段でした。 プロジェクト内の関数を使用するたびに、JSファイル全体を依存関係として含める必要があるため、このファイルは大きすぎて不便な場合があります。 複数のファイルにすることで、柔軟性が増し、そのような依存関係のサイズが減少します。この機能は-Xir-per-moduleコンパイラオプションで利用可能でした。

1.6.20 から、JS IR コンパイラはプロジェクトモジュールごとに個別の.jsファイルをデフォルトで生成します。

プロジェクトを単一の.jsファイルにコンパイルする機能は、以下のGradleプロパティで利用できます。

none
# gradle.properties
kotlin.js.ir.output.granularity=whole-program // `per-module`がデフォルトです

以前のリリースでは、実験的なモジュールごとのモード(-Xir-per-module=trueフラグで利用可能)では、各モジュールでmain()関数が呼び出されました。これは通常の単一.jsモードとは整合性がありません。1.6.20以降、どちらの場合でもmain()関数はメインモジュールでのみ呼び出されます。モジュールがロードされたときに何らかのコードを実行する必要がある場合は、@EagerInitializationアノテーションが付けられたトップレベルプロパティを使用できます。IRコンパイラでトップレベルプロパティの遅延初期化がデフォルトにを参照してください。

Char クラスの最適化

Charクラスは、Kotlin/JSコンパイラによって(インラインクラスと同様に)ボクシングを導入せずに処理されるようになりました。 これにより、Kotlin/JSコードにおける文字操作が高速化されます。

パフォーマンスの向上に加えて、これによりCharがJavaScriptにエクスポートされる方法が変更され、Numberに変換されるようになりました。

エクスポートとTypeScript宣言生成の改善

Kotlin 1.6.20 では、エクスポートメカニズム (@JsExportアノテーション) に関する複数の修正と改善がもたらされており、これにはTypeScript 宣言 (.d.ts) の生成も含まれます。 インターフェースとEnumをエクスポートする機能が追加され、以前報告された一部の特殊なケースでのエクスポート動作が修正されました。 詳細については、YouTrack のエクスポート改善リストを参照してください。

JavaScript から Kotlin コードを使用する方法について詳しくはこちらをご覧ください。

非同期テストに対する @AfterTest の保証

Kotlin 1.6.20 では、Kotlin/JS 上での非同期テストにおいて@AfterTest関数が適切に動作するようになりました。 テスト関数の戻り型が静的にPromiseとして解決される場合、コンパイラは@AfterTest関数の実行を対応するthen()コールバックにスケジュールするようになりました。

セキュリティ

Kotlin 1.6.20 では、コードのセキュリティを向上させるためのいくつかの機能が導入されます。

klib における相対パスの使用

klib形式のライブラリには、適切なデバッグ情報を生成するためのソースファイルのパスを含む、シリアライズされたIR表現が含まれています。 Kotlin 1.6.20 より前では、保存されるファイルパスは絶対パスでした。ライブラリの作者が絶対パスを共有したくない場合があるため、1.6.20 バージョンでは代替オプションが提供されます。

klibを公開し、アーティファクトでソースファイルの相対パスのみを使用したい場合は、-Xklib-relative-path-baseコンパイラオプションを1つ以上のソースファイルのベースパスと共に渡すことができます。

kotlin
tasks.withType(org.jetbrains.kotlin.gradle.dsl.KotlinCompile::class).configureEach {
    // $base はソースファイルのベースパスです
    kotlinOptions.freeCompilerArgs += "-Xklib-relative-path-base=$base"
}
groovy
tasks.withType(org.jetbrains.kotlin.gradle.dsl.KotlinCompile).configureEach {
    kotlinOptions {
        // $base はソースファイルのベースパスです
        freeCompilerArgs += "-Xklib-relative-path-base=$base"
    }
}

Kotlin/JS Gradle プロジェクトの yarn.lock の永続化

この機能は Kotlin 1.6.10 にバックポートされました。

Kotlin/JS Gradle プラグインは、yarn.lock ファイルを永続化する機能を提供するようになりました。これにより、追加の Gradle 設定なしでプロジェクトの npm 依存関係のバージョンをロックすることができます。 この機能は、プロジェクトのルートに自動生成された kotlin-js-store ディレクトリを追加することで、デフォルトのプロジェクト構造に変更をもたらします。 このディレクトリ内に yarn.lock ファイルが保持されます。

kotlin-js-store ディレクトリとその内容をバージョン管理システムにコミットすることを強くお勧めします。 ロックファイルをバージョン管理システムにコミットすることは推奨されるプラクティスです。これは、開発環境やCI/CDサービスなどのすべてのマシンで、アプリケーションがまったく同じ依存関係ツリーでビルドされることを保証するためです。 ロックファイルは、プロジェクトが新しいマシンでチェックアウトされたときに npm 依存関係がサイレントに更新されるのを防ぐことにもなり、これはセキュリティ上の懸念事項です。

Dependabot のようなツールも、Kotlin/JS プロジェクトの yarn.lock ファイルを解析し、依存している npm パッケージが侵害された場合に警告を提供できます。

必要に応じて、ビルドスクリプトでディレクトリ名とロックファイル名の両方を変更できます。

kotlin
rootProject.plugins.withType<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin> {
    rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().lockFileDirectory =
        project.rootDir.resolve("my-kotlin-js-store")
    rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().lockFileName = "my-yarn.lock"
}
groovy
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin) {
    rootProject.extensions.getByType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension).lockFileDirectory =
        file("my-kotlin-js-store")
    rootProject.extensions.getByType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension).lockFileName = 'my-yarn.lock'
}

ロックファイルの名前を変更すると、依存関係検査ツールがファイルを認識しなくなる可能性があります。

npm 依存関係の --ignore-scripts によるデフォルトインストール

この機能は Kotlin 1.6.10 にバックポートされました。

Kotlin/JS Gradle プラグインは、デフォルトで npm 依存関係のインストール中にライフサイクルスクリプトの実行を防止するようになりました。 この変更は、侵害された npm パッケージからの悪意のあるコードの実行の可能性を減らすことを目的としています。

以前の設定に戻すには、build.gradle(.kts)に以下の行を追加してライフサイクルスクリプトの実行を明示的に有効にすることができます。

kotlin
rootProject.plugins.withType<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin> {
    rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().ignoreScripts = false
}
groovy
rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin) {
    rootProject.extensions.getByType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension).ignoreScripts = false
}

Kotlin/JS Gradle プロジェクトの npm 依存関係について詳しくはこちらをご覧ください。

Gradle

Kotlin 1.6.20 では、Kotlin Gradle プラグインに以下の変更が加えられました。

Kotlin コンパイラの実行戦略を定義するためのプロパティ

Kotlin 1.6.20 より前では、Kotlin コンパイラの実行戦略を定義するためにシステムプロパティ-Dkotlin.compiler.execution.strategyを使用していました。 このプロパティは場合によっては不便なことがありました。 Kotlin 1.6.20 では、同じ名前のGradleプロパティkotlin.compiler.execution.strategyとコンパイルタスクプロパティcompilerExecutionStrategyが導入されました。

システムプロパティは引き続き動作しますが、将来のリリースで削除される予定です。

現在のプロパティの優先順位は次のとおりです。

  • タスクプロパティcompilerExecutionStrategyは、システムプロパティおよびGradleプロパティkotlin.compiler.execution.strategyよりも優先されます。
  • Gradleプロパティはシステムプロパティよりも優先されます。

これらのプロパティに割り当てることができるコンパイラ実行戦略は3つあります。

戦略Kotlin コンパイラが実行される場所インクリメンタルコンパイルその他の特性
Daemon独自のデーモンプロセス内はいデフォルト戦略。異なるGradleデーモン間で共有可能
In processGradle デーモンプロセス内いいえGradle デーモンとヒープを共有する可能性あり
Out of process各呼び出しで別プロセスいいえ

したがって、kotlin.compiler.execution.strategyプロパティ(システムとGradleの両方)で利用可能な値は次のとおりです。

  1. daemon(デフォルト)
  2. in-process
  3. out-of-process

gradle.propertiesでGradleプロパティkotlin.compiler.execution.strategyを使用します。

none
# gradle.properties
kotlin.compiler.execution.strategy=out-of-process

compilerExecutionStrategyタスクプロパティに利用可能な値は次のとおりです。

  1. org.jetbrains.kotlin.gradle.tasks.KotlinCompilerExecutionStrategy.DAEMON (デフォルト)
  2. org.jetbrains.kotlin.gradle.tasks.KotlinCompilerExecutionStrategy.IN_PROCESS
  3. org.jetbrains.kotlin.gradle.tasks.KotlinCompilerExecutionStrategy.OUT_OF_PROCESS

build.gradle.ktsビルドスクリプトでタスクプロパティcompilerExecutionStrategyを使用します。

kotlin
import org.jetbrains.kotlin.gradle.dsl.KotlinCompile
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilerExecutionStrategy

// ...

tasks.withType<KotlinCompile>().configureEach {
    compilerExecutionStrategy.set(KotlinCompilerExecutionStrategy.IN_PROCESS)
}

この YouTrack タスクにご意見をお寄せください。

kapt とコルーチンのビルドオプションの非推奨化

Kotlin 1.6.20 では、プロパティの非推奨レベルを変更しました。

  • kapt.use.worker.apiによるKotlinデーモン経由でのkaptの実行機能は非推奨になりました。これはGradleの出力に警告を生成します。 デフォルトでは、kapt は 1.3.70 リリース以降 Gradle worker を使用しており、この方法に固執することをお勧めします。

    今後、kapt.use.worker.apiオプションは削除される予定です。

  • kotlin.experimental.coroutines Gradle DSL オプションとgradle.propertiesで使用されるkotlin.coroutinesプロパティは非推奨になりました。 _中断関数_を使用するか、build.gradle(.kts)ファイルにkotlinx.coroutines依存関係を追加するだけです。

    コルーチンの詳細については、コルーチンガイドを参照してください。

kotlin.parallel.tasks.in.project ビルドオプションの削除

Kotlin 1.5.20 では、ビルドオプションkotlin.parallel.tasks.in.projectの非推奨化を発表しました。 このオプションは Kotlin 1.6.20 で削除されました。

プロジェクトによっては、Kotlin デーモンでの並列コンパイルにはより多くのメモリが必要になる場合があります。 メモリ消費を削減するには、Kotlin デーモンの JVM ヒープサイズを増やしてください

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