Kotlin 1.6.20の変更点

※ソース記事は[こちら](https://kotlinlang.org/docs/whatsnew1620.html)
※Kotlin/JSやKotlin/Nativeについては個人的に使っていないので、割愛します。
Kotlin 1.6.20により、将来の言語機能のプレビューが明らかになり、マルチプラットフォームのプロジェクトのために階層構造がデフォルトになり、他のコンポーネントに進化的な改善がもたらされている。
こちらの[動画](https://youtu.be/8F19ds109-o)でも、変更の短い概要を見ることができる。
# 言語
Kotlin 1.6.20では、２つの言語の特徴を試すことができる。
- Kotlin/JVMのためのコンテキストレシーバーのプロトタイプ
- 明確なnon-nullable型
## Kotlin/JVMのためのコンテキストレシーバーのプロトタイプ
::: note alert
この機能はKotlin/JVMのためにのみ利用可能なプロトタイプである。`-Xcontext-receivers`を有効にすることで、コンパイラは本番コードでは利用できないプレリリースのバイナリを生成するだろう。おもちゃのプロジェクトでのみコンテキストレシーバーを使うこと。[You Track](https://youtrack.jetbrains.com/issues/KT)でフィードバックをいただけると幸いである。
:::

Kotlin 1.6.20では、もはや一つのレシーバーに限定されることはない。より必要なら、宣言にコンテキストレシーバーを追加することで、関数、プロパティ、クラスをコンテキスト依存(あるいはコンテキスト的)にすることができる。コンテキストの宣言は次のように行う。
- すべてのコンテキストレシーバーは、暗黙のレシーバーとして、呼び出し側のスコープに存在する必要がある。
- 宣言されたコンテキストレシーバーは、暗黙のレシーバーとして、本体のスコープにもたらされる。

```Kotlin
interface LoggingContext {
    val log: Logger // このコンテキストはloggerへの参照を提供する
}

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

fun test(loggingContext: LoggingContext) {
    with(loggingContext) {
        // startBusinessOperation()を呼び出すためには、スコープ内でLoggingContextを
        // 暗黙のレシーバーにする必要がある
        startBusinessOperation()
    }
}
```
コンテキストレシーバーをプロジェクトで有効にするには、`-Xcontext-receivers`コンパイラオプションを使う必要がある。この機能と文法についての詳細な説明は、[KEEP](https://github.com/Kotlin/KEEP/blob/master/proposals/context-receivers.md#detailed-design)で見つけられる。
留意すべきは、実装はプロトタイプであるということである。
- `-Xcontext-receivers`を有効にすると、コンパイラは本番コードでは利用できないプレリリースのバイナリを生成するだろう。
- コンテキストレシーバーのためのIDEのサポートは、現状最小限である。

この機能を試し、[こちらのYouTrack課題](https://youtrack.jetbrains.com/issue/KT-42435)で、この機能に対する考えと体験について、共有してほしい。何か問題にぶつかったら、是非[新しい課題を提出](https://kotl.in/issue)していただきたい。
## 明確なnon-nullable型
::: note alert
明確なnon-nullableな型は[Beta](https://kotlinlang.org/docs/components-stability.html)である。これらはほぼ安定しているが、将来、移行手順が必要になるかもしれない。しなければならない変更を最小限にするため、ベストを尽くすつもりである。
:::

ジェネリクスのJavaのクラスとインターフェイスを継承するときに、より良い相互運用性を提供するため、Kotlin 1.6.20では、新しい文法である、`T & Any`とともに、ジェネリクスの型パラメータを利用場所で明確にnon-nullableと印をつけることが可能である。この文法の形は、[交差型](https://en.wikipedia.org/wiki/Intersection_type)の記法から由来しており、現在、`&`の左側のnullableな上限境界を持つ型パラメータと、右側のnon-nullableな`Any`に限定されている。
```Kotlin
fun <T> elvisLike(x: T, y: T & Any): T & Any = x ?: y

fun main() {
    // OK
    elvisLike<String>("", "").length
    // Error: 'null' は、non-null型の値として許可されない
    elvisLike<String>("", null).length

    // OK
    elvisLike<String?>(null, "").length
    // Error: 'null' は、non-null型の値として許可されない
    elvisLike<String?>(null, null).length
}
```
この機能を有効にするには、言語バージョンを`1.7`に設定すること。
```Kotlin
kotlin {
    sourceSets.all {
        languageSettings.apply {
            languageVersion = "1.7"
        }
    }
}
```
[こちらのKEEP](https://github.com/Kotlin/KEEP/blob/c72601cf35c1e95a541bb4b230edb474a6d1d1a8/proposals/definitely-non-nullable-types.md)で、明確なnon-nullable型について、知識を深める。
# Kotlin/JVM
Kotlin 1.6.20では次のものが導入されている。
- JVMインターフェイスのデフォルトメソッドの互換性の改善：[インターフェイスのための新しい`@JvmDefaultWithCompatibility`アノテーション](https://kotlinlang.org/docs/whatsnew1620.html#new-jvmdefaultwithcompatibility-annotation-for-interfaces)と、[`-Xjvm-default`モードでの互換性の変更](https://kotlinlang.org/docs/whatsnew1620.html#compatibility-changes-in-the-xjvm-default-modes)
- JVMバックエンドでの単一のモジュールの並列コンパイルのサポート
- 関数インターフェイスのコンストラクタに対する関数参照のサポート
## インターフェイスのための新しい@&#xFFF0;JvmDefaultWithCompatibilityアノテーション
Kotlin 1.6.20では、新しいアノテーションである、[`@JvmDefaultWithCompatibility`](https://github.com/JetBrains/kotlin/blob/master/libraries/stdlib/jvm/src/kotlin/jvm/JvmDefault.kt#L53)が導入されており、`-Xjvm-default=all`コンパイラオプションとともに使い、すべてのKotlinインターフェイスのうちすべての抽象でないメンバーのために、[JVMインターフェイスデフォルトメソッドを作る](https://kotlinlang.org/docs/java-to-kotlin-interop.html#default-methods-in-interfaces)。
もし`-Xjvm-default=all`無しでコンパイルされたKotlinインターフェイスを使うクライアントがある場合、このオプションでコンパイルされたコードとはバイナリ非互換になるかもしれない。Kotlin 1.6.20より前では、この互換性の問題を避けるため、[推奨するアプローチ](https://blog.jetbrains.com/kotlin/2020/07/kotlin-1-4-m3-generating-default-methods-in-interfaces/#JvmDefaultWithoutCompatibility)は、`-Xjvm-default=all-compatibility`モードでかつ、この種の互換性が不要なインターフェイスに`@JvmDefaultWithoutCompatibility`アノテーションを使うことだった。
このアプローチはいくつか不利な点があった。
- 新しいインターフェイスが追加されたときに、アノテーションを追加することを容易に忘れうる。
- 大抵、公開APIよりも非公開な部分で多くのインターフェイスがあり、コードの中の多くの箇所でこのアノテーションを持つことになる。

今では、`-Xjvm-default=all`モードと、`@JvmDefaultWithCompatibility`アノテーションで、インターフェイスをマークすることができる。これにより、公開API内の全インターフェイスに一度このアノテーションを追加し、新しい公開でないコードには一つもアノテーションを使う必要がなくなる。
[このYou Trackチケット](https://youtrack.jetbrains.com/issue/KT-48217)で、この新しいアノテーションについてのフィードバックを残してほしい。
## -Xjvm-defaultモードでの互換性の変更
Kotlin 1.6.20では、`-Xjvm-default=all`または`-Xjvm-default=all-compatibility`でコンパイルされたモジュールに対して、デフォルトモード(`-Xjvm-default=disable`コンパイラオプション)でモジュールをコンパイルするオプションが追加されている。従来通り、すべてのモジュールが`-Xjvm-default=all`または`-Xjvm-default=all-compatibility`モードを持っている場合にも、コンパイルは成功するだろう。[You Track課題](https://youtrack.jetbrains.com/issue/KT-47000)にフィードバックを残していただくことができる。
Kotlin 1.6.20では、`-Xjvm-default`コンパイラオプションの、`compatibility`と`enable`が非推奨である。互換性に関する他のモードの説明に変更があるが、全体的なロジックは同じ状態のままである。[更新された説明](https://kotlinlang.org/docs/java-to-kotlin-interop.html#compatibility-modes-for-default-methods)を、調べることができる。
Java相互運用性におけるデフォルトメソッドに関しての、より詳細については、[相互運用性ドキュメント](https://kotlinlang.org/docs/java-to-kotlin-interop.html#default-methods-in-interfaces)と[こちらのブログ投稿](https://blog.jetbrains.com/kotlin/2020/07/kotlin-1-4-m3-generating-default-methods-in-interfaces/)を参照のこと。
## JVMバックエンドでの単一のモジュールの並列コンパイルのサポート
::: note alert
 JVMバックエンドでの単一のモジュールの並列コンパイルのサポートは[実験的](https://kotlinlang.org/docs/components-stability.html)である。いつでも中止か変更になるかもしれない。オプトインが必要(詳細は以下)であり、評価のためにのみ、使うべきである。[You Track](https://youtrack.jetbrains.com/issue/KT-46085)でフィードバックをいただけると幸いである。
::: 

[新しいJVM中間表現のバックエンドにおいて、コンパイル時間の改善](https://youtrack.jetbrains.com/issue/KT-46768)が継続的に行われれている。Kotlin 1.6.20では、並列にモジュール内の全ファイルをコンパイルするための実験的なJVM中間表現モードが追加されている。並列コンパイルは、全体のコンパイル時間を最大15%減らすことができる。
`-Xbackend-threads`[コンパイラオプション](https://kotlinlang.org/docs/compiler-reference.html#compiler-options)で、実験的な並列バックエンドモードを有効にすること。このオプションには、次の引数を使う。
- `N`は、使うスレッド数である。これはCPUコア数よりも大きくすべきではない。そうしないとスレッド間のコンテキストスイッチのために、並列化の効果がなくなってしまう。
- `0`はそれぞれのCPUコアで別々のスレッドを分けて使う。

[Gradle](https://kotlinlang.org/docs/gradle.html)では、タスクを並列に実行することができるが、この種の並列化は、プロジェクト(あるいはプロジェクトの大部分)がGradleの視点から、大きな一つのタスクであるときには、あまり役に立たない。もし非常に大きなモノシリックなモジュールがある場合は、より早くコンパイルするには、並列コンパイルを使うこと。もしプロジェクトが多くの小さなモジュールから構成されており、Gradleによって並列にビルドされている場合は、別の並列化レイアが追加されることで、コンテキストスイッチにより、性能が劣化するかもしれない。

::: note
並列コンパイルはいくつかの制約がある。
- kaptは中間表現バックエンドを無効にするため、kaptとは連携しない。
- 設計により、より多くのJVMヒープ領域を必要とする。ヒープの量はスレッド数に比例する。
:::
## 関数インターフェイスのコンストラクタに対する関数参照のサポート
::: note alert
 関数インターフェイスのコンストラクタに対する関数参照のサポートは[実験的](https://kotlinlang.org/docs/components-stability.html)である。いつでも中止か変更になるかもしれない。オプトインが必要(詳細は以下)であり、評価のためにのみ、使うべきである。[You Track](https://youtrack.jetbrains.com/issue/KT-47939)でフィードバックをいただけると幸いである。
::: 

関数インターフェイスのコンストラクタに対する関数参照のサポートにより、コンストラクタ関数を持つインターフェイスから、[関数型のインターフェイス](https://kotlinlang.org/docs/fun-interfaces.html)へ移行する、ソースレベルで互換性のある方法が追加される。
次のコードを考えてみよう。
```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`関数は、`DeprecationLevel.HIDDEN`を持つ`@Deprecated`アノテーションがマークして、バイナリ互換性を保持すること。
```Kotlin
@Deprecated(message = "非推奨についてのメッセージを追加する", level = DeprecationLevel.HIDDEN)
fun Printer(...) {...}
```
この機能を有効にするには、`-XXLanguage:+KotlinFunInterfaceConstructorReference`コンパイラオプションを使うこと。
# セキュリティ ※割愛
# Gradle
Kotlin 1.6.20では、次の変更がKotlin Gradleプラグインにもたらされている。
- Kotlinコンパイラの実行戦略を定義する、新しい[`kotlin.compiler.execution.strategyとcompilerExecutionStrategy`プロパティ](https://kotlinlang.org/docs/whatsnew1620.html#properties-for-defining-kotlin-compiler-execution-strategy)
- [`kapt.use.worker.api`、`kotlin.experimental.coroutines`、`kotlin.coroutines`オプションの非推奨](https://kotlinlang.org/docs/whatsnew1620.html#deprecation-of-build-options-for-kapt-and-coroutines)
- [`kotlin.parallel.tasks.in.project`ビルドオプションの削除](https://kotlinlang.org/docs/whatsnew1620.html#removal-of-the-kotlin-parallel-tasks-in-project-build-option)
## Kotlinコンパイラ実行戦略を定義するプロパティ
Kotlin 1.6.20より前は、Kotlinコンパイラ実行戦略を定義するために、`-Dkotlin.compiler.execution.strategy`システムプロパティが使われていた。このプロパティはいくつかのケースで使いづらいかもしれなかった。Kotlin 1.6.20では、同じ名前である、`kotlin.compiler.execution.strategy`Gradleプロパティと、コンパイルタスクプロパティである`compilerExecutionStrategy`が導入されている。
システムプロパティはまだ動作するが、将来のリリースで削除されるだろう。
プロパティの現在の優先順位は次のとおりである。
- タスクプロパティである`compilerExecutionStrategy`は、システムプロパティと、Gradleプロパティである`kotlin.compiler.execution.strategy`よりも優先する。
- Gradleプロパティはシステムプロパティよりも優先する。

これらのプロパティには３つのコンパイラ実行戦略を割り当てることができる。
|戦略  |Kotlinコンパイラが実行される場所|インクリメンタルコンパイル |その他の特徴|
|---|---|---|---|
|Daemon  |自身のデーモンプロセス内  |可能  |デフォルトの戦略。異なるGradleデーモン間で共有可能|
|In process|Gradleデーモンプロセス内  |不可  |Gradleデーモンとヒープを共有可能|
|Out of process|それぞの呼び出しごとに別プロセス内|不可  |-|

さらに、`kotlin.compiler.execution.strategy`プロパティ(システムとGradle両方)に対して利用可能な値は以下である。
1. `daemon`(デフォルト)
1. `in-process`
1. `out-of-process`

`gradle.properties`内で、`kotlin.compiler.execution.strategy`Gradleプロパティを次のように使うこと。
```
# gradle.properties
kotlin.compiler.execution.strategy=out-of-process
```
`compilerExecutionStrategy`タスクプロパティの利用可能な値は以下である。
1. `org.jetbrains.kotlin.gradle.tasks.KotlinCompilerExecutionStrategy.DAEMON`(デフォルト)
1. `org.jetbrains.kotlin.gradle.tasks.KotlinCompilerExecutionStrategy.IN_PROCESS`
1. `org.jetbrains.kotlin.gradle.tasks.KotlinCompilerExecutionStrategy.OUT_OF_PROCESS`

`compilerExecutionStrategy`タスクプロパティは、`build.gradle.kts`ビルドスクリプト内で、次のように使うこと。
```Kotlin
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilerExecutionStrategy

// …

tasks.withType<KotlinCompile>().configureEach {
    compilerExecutionStrategy.set(KotlinCompilerExecutionStrategy.IN_PROCESS)
}
```
[こちらのYou Track課題](https://youtrack.jetbrains.com/issue/KT-49299)にフィードバックを是非残してもらいたい。
## kaptとコルーチンのためのビルドオプションの非推奨
Kotlin 1.6.20では、プロパティの非推奨レベルが変更された。
- `kapt.use.worker.api`で、Kotlinデーモン経由で、kaptを実行できることが非推奨になった。現在は、Gradleの出力に警告が生成される。デフォルトでは、1.3.70のリリースから[kaptはGradleワーカーを使って](https://kotlinlang.org/docs/kapt.html#running-kapt-tasks-in-parallel)おり、この方法を踏襲することを推奨する。
将来のリリースでは、`kapt.use.worker.api`オプションは削除される予定である。
- `kotlin.experimental.coroutines` Gradle DSLオプションと`gradle.properties`内の`kotlin.coroutines`プロパティは非推奨になった。`build.gradle(.kts)`ファイルで、単にsuspend関数を使うか、`kotlinx.coroutine`の依存性を追加すること。
[`Coroutineガイド`](https://kotlinlang.org/docs/coroutines-guide.html)でコルーチンについての知識を深める。
##  kotlin.parallel.tasks.in.projectビルドオプションの削除
Kotlin 1.5.20では、[` kotlin.parallel.tasks.in.project`ビルドオプションの非推奨]()がアナウンスされた。このオプションはKotlin 1.6.20で削除された。
プロジェクトによっては、Kotlinデーモンで並列コンパイルするのは、より資金が必要になるかもしれない。資金の浪費を減らすため、[Kotlinデーモンのヒープサイズを増やすこと](https://kotlinlang.org/docs/gradle.html#setting-kotlin-daemon-s-jvm-arguments)。
Kotlin Gradleプラグインで、[現在サポートされているコンパイラオプション](https://kotlinlang.org/docs/gradle.html#compiler-options)についての知識を深める。