※ソース記事はこちら
※Kotlin/JSやKotlin/Nativeについては個人的に使っていないので、割愛します。
Kotlin 1.6.0では、新しい言語機能、既存の機能に対する最適化と改善、Kotlin標準ライブラリに対する多くの改善が導入されている。
リリースブログ投稿でも、変更点の概要を知ることができる。
言語
Kotlin 1.6.0では、以前のリリース1.5.30の中でプレビューとして導入されているいくつかの言語機能の安定化がもたらされている。
- enum、sealed、Booleanが主語の網羅的なwhen式の安定化
- 基底型としてのsuspend関数の安定化
- suspend変換の安定化
- アノテーションクラスのインスタンス化の安定化
以下のような、様々な型の推論の改善と、クラス型のパラメータについてのアノテーションのサポートも含まれる。
- 再起的なジェネリック型のための型推論の改善
- builder推論の変更
- クラス型パラメータについてのアノテーションのサポート
enum、sealed、Booleanが主語の網羅的なwhen式の安定化
網羅的なwhen式には、その主語のすべてのありうる型あるいは値の分岐、またはいくつかの型とelseの分岐が含まれる。すべてのありうるケースをカバーすることによりコードがより安全になる。
when
式によるふるまいを首尾一貫とするために、網羅的でないwhen
はすみやかに禁止されるようになる。スムーズな移行のため、Kotlin 1.6.0ではenum、sealed、Booleanが主語である網羅的でない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'式が網羅的である必要がある。
is Contact.PhoneCall -> 42
}
fun sendMessage(contact: Contact, message: String) {
// 1.6.0からの警告
// Warning: 網羅的でないBooleanについてのwhen式は1.7で禁止になる予定。代わりにfalseの分岐か、elseの分岐を追加すること。
when(message.isEmpty()) {
true -> return
}
// Warning: 網羅的でないsealedクラス/インターフェイスは1.7で禁止になる予定。代わりにis TextMessageの分岐か、elseの分岐を追加すること。
when(contact) {
is Contact.PhoneCall -> TODO()
}
}
変更とその効果のより詳細な説明のため、このYou trackチケットを参照のこと。
基底型としてのsuspend関数の安定化
suspend関数型の実装は、Kotlin 1.6.0で安定になっている。プレビューは1.5.30で利用可能だった。
この機能は、Kotlinコルーチンを使い、suspend関数型を受けるAPIを設計するときに役に立つだろう。今では、suspend関数型を実装する独立したクラスの中でふるまいを囲むことにより、コードをスリム化することができる。
class MyClickAction : suspend () -> Unit {
override suspend fun invoke() { TODO() }
}
fun launchOnClick(action: suspend () -> Unit) {}
ラムダとsuspend関数への参照だけが事前に許可される場所で、このクラスのインスタンスを使うことができる。launchOnClick(MyClickAction())
現在、実装の詳細から、二つの制限が存在する。
- 基底型のリストのうち、通常の関数型とsuspend関数型を混在することができない。
- 複数のsuspend関数の基底型を使うことができない。
suspend変換の安定化
Kotlin 1.6.0から、通常から、suspend関数型への安定した変換が導入されている。1.4.0から、この機能は、関数リテラルと、関数への参照がサポートされていた。※
1.6.0とともに、どのような式の形とも連携するようになった。呼び出しの引数として、suspend関数型が想定される場所で、ふさわしい通常の関数型のどのような式も渡すことができる。コンパイラが自動的に暗黙の変換を実行するだろう。
fun getSuspending(suspending: suspend () -> Unit) {}
fun suspending() {}
fun test(regular: () -> Unit) {
getSuspending { } // OK
getSuspending(::suspending) // OK
getSuspending(regular) // OK
}
アノテーションクラスのインスタンス化の安定化
Kotlin 1.5.30では、JVMプラットフォームにおいて、アノテーションクラスのインスタンス化のサポートが導入された。1.6.0とともに、この機能はKotlin/JVMとKotlin/JS両方でデフォルトで利用可能になっている。
このkEEPで、アノテーションクラスのインスタンス化についての知識を深める。
再起的なジェネリック型のための型推論の改善
Kotlin 1.5.30では、再起的なジェネリック型のための型推論の改善が導入され、型引数が、対応する型パラメータの上限境界においてのみ、推論が許された。この改善はコンパイラオプションで利用可能だった。1.6.0以降のバージョンでは、デフォルトで有効である。
// 1.5.30以前
val containerA = PostgreSQLContainer<Nothing>(DockerImageName.parse("postgres:13-alpine")).apply {
withDatabaseName("db")
withUsername("user")
withPassword("password")
withInitScript("sql/schema.sql")
}
// 1.5.30かつコンパイラオプション有り、または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
アノテーションを適用することなく、自分自身のbuilderを書くことができる。
留意すべきは、これらのbuilderのクライアントは、同じ-Xenable-builder-inference
コンパイラオプションを必要とすることである。
-
-Xenable-builder-inference
とともに、もし通常のが型推論が型について十分な情報が得られない場合、builder推論が自動的に有効になる。
クラス型パラメータについてのアノテーションのサポート
クラス型パラメータについてのアノテーションのサポートは、このようなものである。
@Target(AnnotationTarget.TYPE_PARAMETER)
annotation class BoxContent
class Box<@BoxContent T> {}
すべての型パラメータについてのアノテーションは、JVMバイトコードに発行され、アノテーションプロセッサはそれらを使うことができる。
動機付けとなるユースケースとして、このYou Trackチケットを読むこと。
より長期にわたる過去のAPIバージョンのサポート
Kotlin 1.6.0から、現在の安定板とともに、2つに代わり、3つの過去のAPIバージョンがサポートされる。現在、1.3、1.4、1.5、1.6がサポートされている。
Kotlin/JVM
Kotlin/JVMにとって、1.6.0からは、コンパイラはJVM 17に対応するバイトコードバージョンでクラスが生成される。新しい言語バージョンには、ロードマップにあった、最適化されたdelegateプロパティと繰り返し可能なアノテーションも含まれる。
- JVMターゲット1.8のための、実行時に保持される繰り返し可能なアノテーション
- 与えられたKPropertyインスタンスのget/setを呼ぶ最適化されたdelegateプロパティ
JVMターゲット1.8のための、実行時に保持される繰り返し可能なアノテーション
Java 8では繰り返し可能なアノテーションが導入され、単一のコード要素に対して複数回適用されることができる。この機能は、Javaコード上で、2つの宣言を必要とする。それは@java.lang.annotation.Repeatable
の印がついた繰り返し可能なアノテーションそのものと、値を保持するためのアノテーションである。
Kotlinも繰り返し可能なアノテーションは持っているが、繰り返し可能にするために、アノテーション宣言で、@kotlin.annotation.Repeatableが必要なだけである。1.6.0より前は、この機能はSOURCE
保持のみサポートされており、Javaの繰り返し可能アノテーションと整合性が無かった。Kotlin 1.6.0はこの制限を除いている。
@kotlin.annotation.Repeatable
は今は、どんな保持も受け入れ、KotlinとJava両方でアノテーションを繰り返し可能にする。Javaの繰り返し可能アノテーションは今では、Kotlin側からもサポートされる。
値を保持するためのアノテーションを宣言することができる一方で、必要ではない。例えば、
- もし、
@Tag
アノテーションが@kotlin.annotation.Repeatable
と印がついていた場合、Kotlinコンパイラは、@Tag.Container
の名前を借りて保持するアノテーションを自動的に生成する。
@Repeatable
annotation class Tag(val name: String)
// コンパイラは@Tag.Containerという名で保持するアノテーションを生成する
- 保持するアノテーションにカスタムな名前をつけるには、
@kotlin.jvm.JvmRepeatable
メタアノテーションを適用し、明示的に宣言した保持するアノテーションを引数として渡す。
@JvmRepeatable(Tags::class)
annotation class Tag(val name: String)
annotation class Tags(val value: Array<Tag>)
Kotlinのリフレクションは、今では、新しい関数KAnnotatedElement.findAnnotations()
を経由してKotlinとJava両方の繰り返し可能なアノテーションをサポートする。
このKEEPにて、繰り返し可能なアノテーションについて、知識を深める
与えられたKPropertyインスタンスのget/setを呼ぶ最適化されたdelegateプロパティ
生成されたJVMバイトコードを、$delegate
フィールドを省略し、参照されているプロパティへ直接アクセスするするよう生成することで、最適化が行われた。
例えば、以下のコード
class Box<T> {
private var impl: T = ...
var content: T by ::impl
}
Kotlinは最早content$delegate
フィールドを生成しない。content
変数のプロパティアクセスは、impl
変数を直接呼び出し、delegateプロパティのgetValue/setValue
演算子をスキップし、そのためKProperty
型のプロパティ参照オブジェクトの必要を避けている。
Googleの同僚の実装に感謝する!
delegateプロパティについての知識を深める
Kotlin Gradleプラグイン
Kotlin 1.6.0では、KotlinGradleSubplugin
クラスの非推奨レベルが「エラー」に変更された。このクラスはコンパイラープラグインを書くために使われていた。次のリリースでは、このクラスは削除される予定である。代わりにKotlinCompilerPluginSupportPlugin
クラスを使うこと。
kotlin.useFallbackCompilerSearch
ビルドオプションと、noReflect
とincludeRuntime
コンパイラオプションが削除された。useIR
コンパイラオプションは、隠され、将来のリリースでは削除されるだろう。
Kotlin Gradleプラグインで、現在サポートされるコンパイラオプションについての知識を深める。
標準ライブラリ
標準ライブラリの新しい1.6.0バージョンでは、実験的機能を安定化し、新しい機能を導入し、プラットフォーム間のふるまいを統一している。
- 新しいreadline関数
- 安定したtypeOf()
- 安定したコレクションビルダー
- 安定したDuration API
- RegExをシーケンスに分割
- 整数のビットローテーション演算
- JSのreplace()とreplaceFirst()のための変更
- 既存PAPIの改善
- 非推奨
新しいreadline関数
Kotlin 1.6.0では、標準入力を扱う新しい関数、readln()
とreadlnOrNull()
が提供されている。
現在は、新しい関数はJVMとNative向けのプラットフォームだけ利用可能である。
古いバージョン | 1.6.0の選択肢 | 使い方 |
---|---|---|
readline()!! |
readln() |
標準入力から行を読み、それを戻すか、EOFに到達したらRuntimeException をスローする。 |
readline() |
readlnOrNull() |
標準入力から行を読み、それを戻すか、EOFに到達したらnull を返却する。 |
行を読むときに!!
を使う必要を排除するは、新規参入者の経験を向上させ、Kotlinを教えることを簡単にすると考えている。read-line演算の名前を対応する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()
は次第に非推奨になることが計画されている。
安定したtypeOf()
バージョン1.6.0では、主要なロードマップ項目の終了しているものの一つである、安定したtypeOf()
がもたらされている。
1.3.40から、typeOf()
は実験的APIとしてJVMプラットフォーム上で利用可能だった。現在は、すべてのKotlinプラットフォームで使うことができ、コンパイラが推測するすべてのKotlin型のKtype
表現を得ることができる。
import kotlin.reflect.typeOf
inline fun <reified T> renderType(): String {
val type = typeOf<T>()
return type.toString()
}
fun main() {
val fromExplicitType = typeOf<Int>() // kotlin.Int
val fromReifiedType = renderType<List<Int>>() // kotlin.collections.List<kotlin.Int>
}
安定したコレクションビルダー
Kotlin 1.6.0ではコレクションビルダー関数が安定版に昇格された。コレクションビルダーによって返却されるコレクションは、現在は読み取り専用の状態で、シリアライズ可能である。
現在はopt-inアノテーション無しで、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]
}
安定した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
のような拡張プロパティを復活させている。しかし、その適用性を制限したいため、Duraion
クラスのコンパニオンに置いている。IDEは引き続きコード補完で拡張を提案し、自動的にコンパニオンからのimportを挿入するかもしれないが、将来はDuraion
型が期待されるケースではその振る舞いは制限されることが計画されている。
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
}
Duraion.seconds(Int)
のような、以前導入されたコンパニオン関数と、Int.seconds
のような非推奨のトップレベルの拡張を、Duration.Companion
の新しい拡張で置き換えることを推奨している。
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()
とreplaceFirst()
の正規表現関数は、置換するの文字列にグループ参照が含まれるとき、JavaとJSで振る舞いが異なっていた。すべてのターゲットプラットフォームで、ふるまいを一貫させるため、JSの実装を変更した。置換する文字列の、${name}
と${index}
の発生は、指定したインデックスと名前を持つ、キャプチャされたグループに対応する結果に対してのものに置き換えられている。
-
${index}
- 「$」の後の最初の桁数字は、常にグループ参照の一部として扱われる。続く桁数字は、それが有効なグループ参照を形成する場合のみ、index
に組み込まれる。数字の「0」-「9」のみが、グループ参照のとりうる要素としてみなされる。留意すべきは、キャプチャされたグループのインデックスは「1」から始まる。インデックス「0」はマッチした全体を意味する。 -
${name}
-name
は、英文字「a」-「z」か、数字「0」-「9」で構成される。最初の文字は英文字でなければならない。
置換パターン内の名前付きグループは現座、JVMにおいてのみサポートされている。
- 置換文字列に、リテラルとして後続文字を含めるには、バックスラッシュ文字
\
を使う。
fun main() {
println(Regex("(.+)").replace("Kotlin", """\$ $1""")) // $ Kotlin
println(Regex("(.+)").replaceFirst("1.6.0", """\\ $1""")) // \ 1.6.0
}
置換文字列がリテラル文字として扱われる必要がある場合、Regex.escapeReplacement()
を使うことができる。
既存APIの改善
- バージョン1.6.0では、
Comparable.compareTo()
のための二項拡張関数が追加された。現在、並び替えで2つのオブジェクトを比べるために、二項形式を使うことができる。
class WrappedText(val text: String) : Comparable<WrappedText> {
override fun compareTo(other: WrappedText): Int =
this.text compareTo other.text
}
- JSの
Regex.replace()
は、すべてにプラットフォームにわたって実装を統一するため、現在ではインラインでもない。 -
isBlank()
CharSequence関数と同様に、compareTo()
とequals()
String関数は、JSにおいて現在、JVMと同様にふるまう。以前はASCII文字以外となると、ずれが存在していた。
非推奨
Kotlin 1.6.0では、JSのみの標準ライブラリのための警告から、非推奨サイクルを開始している。
concat(),match(),matchs()文字列関数
- 与えられた他のオブジェクトの文字列同士の表現に集中するため、
concat()
の代わりにplus()
を使う。 - 入力値のうち、正規表現のすべての出現を見つけるため、
String.match(regex: String)
の代わりにfindAll()
を使う。 - 正規表現が入力値全体にマッチするかどうかチェックするために、
String.matches(regex: String)
の代わりにmatches()
を使う。
比較関数を取る配列のsort()
比較関数を引数に、順序良く配列をソートする、Array<out T>.sort()
関数、ByteArray.sort()
、ShortArray.sort()
、IntArray.sort()
、LongArray.sort()
、FloatArray.sort()
、DoubleArray.sort()
、CharArray.sort()
インライン関数は、非推奨になった。配列のソートのために、他の標準ライブラリの関数を使用すること。
参考のため、コレクションの順序を参照のこと。
ツール
Kover - Kotlinのためのコードカバレッジ
Kover Gradleプラグインは、実験的である。GitHubでフィードバックをいただければ幸いに思う。
Kotlin 1.6.0とともにKoverが導入されている。これはIntelliJとJaCoCoコードカバレッジエージェントのためのGradleプラグインである。これはインライン関数を含め、すべての言語の構成物と連携する。
Koverについて、GitHubリポジトリか、こちらの映像で知識を深める。
コルーチン 1.6.0-RC
kotlin.coroutines
1.6.0-RCが、多くの機能と改善とともに公開されている。
- 新しいKotlin/Nativeのメモリマネージャ
- 追加のスレッドを作らずに並列性を制限するdispatcher views APIの導入
- Java 6からJava 8ターゲットへの移行
- 新しいリメイクされたAPIとマルチプラットフォームがサポートされた
kotlinx-coroutines-test
- コルーチンで、
ThreadLocal
変数にアクセスするCopyableThreadContextElement
の導入
変更履歴で知識を深める
Kotlin 1.6.0への移行
IntelliJ IDEAとAndroid Studioでは、Kotlinプラグインが1.6.0が一度利用可能になると、アップデートが提案される。
既存のプロジェクトをKotlin 1.6.0に移行するには、Kotlinバージョンを1.6.0
に変更し、GradleかMavenプロジェクトを再インポートする。
Kotlin 1.6.0にアップデートする方法を学ぶ
Kotlin 1.6.0で新しいプロジェクトを開始するには、Kotlinプラグインを更新し、ファイル|新規|プロジェクトから、Project Wizardを起動する。
新しいコマンドラインコンパイラは、GitHubリリースページでダウンロード可能である。
Kotlin 1.6.0は機能リリースであり、そのため言語の古いバージョンで書かれたコードと互換性のない変更をもたらす。Kotlin 1.6互換性ガイド内でそのような変更の詳細なリストを参照のこと。