※ソース記事はこちら
※Kotlin/JS、Kotlin/Nativeについては割愛します。
Kotlin 1.7.20用のIDEサポートがIntelliJ IDEA 2021.3,2022.1,2022.2で利用可能
リリース日:2022/9/29
Kotlin 1.7.20がリリースされている。こちらに、このリリースからいくつかのハイライトがある。
- 新しいKotlin K2 コンパイラが
all-open
、SAM with receiver、Lombok等のコンパイラプラグインをサポート - 上限を含まない範囲をつくるための
..<
演算子のプレビューの導入 - 新しいKotlin/Nativeのメモリマネージャーが現在デフォルトで有効
- JVM用の新しい実験的機能の導入:ジェネリックの基底型を持つインラインクラス
このビデオで、変更の短い概要を見つけることができる。
Kotlin K2コンパイラプラグインのサポート
KotlinチームはK2コンパイラの安定化を続けている。K2はまだ、Alpha状態(Kotlin 1.7.0リリースでアナウンスされているように)であるが、今ではいくつかのコンパイラプラグインをサポートしている。このYouTrack問題をフォローすることで、新しいコンパイラについてのKotlinチームからの更新を得ることができる。
この1.7.20のリリースから、Kotlin K2コンパイラは次のプラグインをサポートしている。
all-open
no-arg
- SAM with receiver
- Lombok
- Atomic FU
jvm-abi-gen
AlphaバージョンのKotlin K2コンパイラはJVMプロジェクトとのみ連携する。Kotlin/JS、Kotlin/Nativeあるいはマルチプラットフォームプロジェクトはサポートしていない。
次のビデオで、新しいコンパイラとそのメリットについての知識を深める。
Kotlin K2コンパイラを有効にする方法
Kotlin K2コンパイラを有効にしてテストするために、次のコンパイラオプションを使うこと。
-Xuse-k2
自分のJVMプロジェクトで性能向上を確認し、古いコンパイラの結果と比較することができる。
新しいK2コンパイラについてフィードバックを残す
我々はどんな形でもフィードバックに本当に感謝している。
- Kotlin SlackでK2開発者へ直接フィードバックを提供:招待を受け、#k2-early-adaoptersチャンネルに参加する
- 我々のissue trackerで、新しいK2コンパイラで直面した問題を報告
- JetBrainsにK2使用についての匿名データの収集を許可するため、使用統計の送信オプションを有効化
言語
Kotlin 1.7.20では、ビルダー推論における制限と、新しい言語機能のプレビューが導入されている。
- 上限を含まない範囲をつくるための
..<
演算子のプレビュー - 新しいdataオブジェクトの宣言
- builder型推論の制限
上限を含まない範囲をつくるための..<
演算子のプレビュー
新しい演算子は実験的であり、IDEサポートが制限されている。
今回のリリースでは、新しい..<
演算子が導入されている。Kotlinには値の範囲を表現するために..
演算子がある。新しい..<
演算子はuntil
関数のようにふるまい、上限を含まない範囲(※未満)を定義するのに役に立つ。
我々の調査によれば、この新しい演算子が上限を含まない範囲を表現し、上限が含まれないことを明確にするのにより良い仕事をする。
こちらにwhen
式で..<
演算子を使う例がある。
when (value) {
in 0.0..<0.25 -> // first quarter ※0.25は含まない
in 0.25..<0.5 -> // second quarter ※0.5は含まない
in 0.5..<0.75 -> // third quarter ※0.75は含まない
in 0.75..1.0 -> // last quarter <- これは上限を含む範囲
}
標準ライブラリのAPI変更
次の新しい型と演算子が共通のKotlin標準ライブラリにあるkotlin.ranges
パッケージに導入されるだろう。
新しいOpenEndRangeインターフェイス
上限を含まない範囲を表す新しいインターフェイスは、既存のClosedRange<T>
インターフェイスと非常に似ている。
interface OpenEndRange<T : Comparable<T>> {
// 下限
val start: T
// 上限、範囲には含まれない
val endExclusive: T
operator fun contains(value: T): Boolean = value >= start && value < endExclusive
fun isEmpty(): Boolean = start >= endExclusive
}
既存の反復可能範囲へのOpenEndRangeの実装
開発者が、上限を含まない範囲が必要なとき、現在は同一の値を使って上限を含む反復可能範囲を効率的に作るため、until
関数を使っている。これらの範囲をOpenEndRange<T>
を取る新しいAPIで容認するため、既存の反復可能な範囲(IntRange
,LongRange
,CharRange
,UIntRange
,ULongRange
)でインターフェイスを実装したい。そのため、それらはClosedRange<T>
とOpenEndRange<T>
インターフェイス両方を同時に実装するだろう。
class IntRange : IntProgression(...), ClosedRange<Int>, OpenEndRange<Int> {
override val start: Int
override val endInclusive: Int
override val endExclusive: Int
}
標準型用のrangeUntil演算子
現在rangeTo
演算子によって定義されているのと同じ型と組み合わせに対して、rangeUntil
演算子が提供されるだろう。我々はプロトタイプのため、それらを拡張関数として提供しているが、上限を含まない範囲のAPIを安定化する前に、それらをメンバにする計画がある。
..<演算子の有効化方法
..<
演算子を使うか、自分自身の型のためにその演算子規約を実装するには、-language-version 1.8
コンパイラオプションを有効にする。
標準型の上限を含まない範囲をサポートするために導入された新しいAPI要素は、実験的な標準API用としていつものように、次のようなオプトインが必要である。
@OptIn(ExperimentalStdlibApi::class)
あるいは、コンパイラオプションで、-opt-in=kotlin.ExperimentalStdlibApi
を使うこともできる。
このKEEP文書で新しい演算子について続きを読む
data objectによるシングルトンとsealedクラス階層のための改善された文字列表現
data objectは実験的であり、IDEサポートが制限されている。
このリリースでは、data object
という新しいobject
宣言の型が導入されている。data objectは概念的には通常のobject
と同一にふるまうが、すぐに使えるきれいなtoString
表現を持っている。
package org.example
object MyObject
data object MyDataObject
fun main() {
println(MyObject) // org.example.MyObject@1f32e575
println(MyDataObject) // MyDataObject
}
このことにより、dataオブジェクト
宣言がsealdクラス階層にとって完全になり、そこでそれらをdata class
とともに使うことができる。このスニペットでは、EndOfFile
を単なるobject
の代わりにdata object
とすることで、手動でオーバーライドする必要なく、整形されたtoString
を得られることを意味しており、付随するdata class
宣言と調和を維持している。
sealed class ReadResult {
data class Number(val value: Int) : ReadResult()
data class Text(val value: String) : ReadResult()
data object EndOfFile : ReadResult()
}
fun main() {
println(ReadResult.Number(1)) // Number(value=1)
println(ReadResult.Text("Foo")) // Text(value=Foo)
println(ReadResult.EndOfFile) // EndOfFile
}
data objectの有効化方法
自分のコードでdata object宣言を使うには、-language-version 1.8
コンパイラオプションを有効にする。Gradleプロジェクトでは、build.gradle(.kts)
に次のように追加することで可能である。
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
// . . .
kotlinOptions.languageVersion = "1.8"
}
個別のKEEP文書で、data objectについて続きを読み、その実装に関してフィードバックを共有する。
新しいbuilder推論の制限
Kotlin 1.7.20では、自分のコードに影響を与えうるbuilder推論の使用に関して、いくつかの主要な制限を設けている。これらの制限は、builderラムダ関数を含むコードに当てはまり、それはラムダ自身を解析しないとパラメータを導出することができないためである。そのパラメータは引数として使われている。現在では、コンパイラはそのようなコードに対して常にエラーを表示し、明示的に型を指定するように依頼しているだろう。
これは破壊的な変更であるが、我々の調査によれば、これらのケースは非常にまれであり、制限はあなたのコードに影響しないはずである。もしその場合は、次のケースを考慮すること。
- 拡張を持つbuilder推論がメンバを隠している
もしあなたのコードが、同一の名前で拡張関数を含んでおり、builder推論の間使われていると、コンパイラはエラーを表示するだろう。
class Data {
fun doSmth() {} // 1
}
fun <T> T.doSmth() {} // 2
fun test() {
buildList {
this.add(Data())
this.get(0).doSmth() // 2に解決し、エラーとなる
}
}
コードを修正するには、型を明示的に指定しなければならない。
class Data {
fun doSmth() {} // 1
}
fun <T> T.doSmth() {} // 2
fun test() {
buildList<Data> { // 型引数
this.add(Data())
this.get(0).doSmth() // 1に解決する
}
}
- 複数のラムダを持つbuilder推論で、型引数が明示的に指定されていない
もしbuilder推論で2つ以上のラムダがある場合、それらは型に影響を及ぼす。エラーを防ぐため、コンパイラは型を指定することを必要とする。
fun <T: Any> buildList(
first: MutableList<T>.() -> Unit,
second: MutableList<T>.() -> Unit
): List<T> {
val list = mutableListOf<T>()
list.first()
list.second()
return list
}
fun main() {
buildList(
first = { // this: MutableList<String>
add("")
},
second = { // this: MutableList<Int>
val i: Int = get(0)
println(i)
}
)
}
エラーを修正するには、型を明示的に指定し、型のミスマッチを修正しなければならない。
fun main() {
buildList<Int>(
first = { // this: MutableList<Int>
add(0)
},
second = { // this: MutableList<Int>
val i: Int = get(0)
println(i)
}
)
}
もし、上であげたケースが見つからない場合、われわれのチームに問題を提出してほしい。
builder推論の更新についてのさらなる情報のため、このYouTrack問題を参照のこと。
Kotlin/JVM
Kotlin 1.7.20では、ジェネリックインラインクラスが導入され、delegatedプロパティのためのバイトコードの最適化が追加され、kaptのスタブ生成タスクでIRがサポートされ、kaptですべての最新のKotlinの機能を使うことができるようになっている。
ジェネリックインラインクラス
Kotlin 1.7.20では、JVMインラインクラスの基底型が型パラメータであることが許可されている。コンパイラはそれをAny?
あるいは一般的には型パラメータの上方境界にマップする。
次の例を考えてみよう。
@JvmInline
value class UserId<T>(val value: T)
fun compute(s: UserId<String>) {} // コンパイラはfun compute-<ハッシュコード>(s: Any?)を生成する
この関数はインラインクラスをパラメータとして受け取る。パラメータは型引数ではなく、上方境界にマップされる。
この機能を有効にするには、-language-version 1.8
コンパイラオプションを使用する。
YouTrackにて、この機能についてのフィードバックに感謝する。
delegatedプロパティのより多くの最適化ケース
Kotlin 1.6.0で、$delegate
フィールドを省略し、参照されるプロパティへの直接のアクセスを生成することで、プロパティに対するdelegateのケースが最適化された。1.7.20では、より多くのケースで最適化を実装した。現在は以下の場合に$delegate
が省略されるだろう。
- 名前つきオブジェクト
object NamedObject {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String = ...
}
val s: String by NamedObject
- 同じモジュール内でバッキングフィールドとデフォルトgetterを持つfinalな
val
プロパティ
val impl: ReadOnlyProperty<Any?, String> = ...
class A {
val s: String by impl
}
- 定数表現、enum定数、
this
、null
のいずれか。ここではthis
の例を挙げる。
class A {
operator fun getValue(thisRef: Any?, property: KProperty<*>) ...
val s by this
}
delegatedプロパティについて、知識を深める。
この機能について、YouTrackでのフィードバックに感謝する。
kaptのスタブ生成タスクでのJVM IRバックエンドのサポート
kaptのスタブ生成タスクでのJVM IRバックエンドのサポートは、実験的機能である。いつでも削除されたり、変更されるかもしれない。オプトインが必要(詳細は下記)であり、評価目的のためにのみ使うべきである。
1.7.20より前では、kaptのスタブ生成タスクは古いバックエンドを使用しており、繰り返し可能なアノテーションはkaptとは連携しなかった。Kotlin 1.7.20で、kaptのスタブ生成タスクでJVM IRバックエンドのサポートを追加した。これにより、kaptで繰り返し可能なアノテーションを含むすべての最新のKotlinの機能を使用できるようになる。
kaptでIRバックエンドを使用するには、gradle.properties
ファイルで次のオプションを追加する。
kapt.use.jvm.ir=true
YouTrackで、この機能についてのフィードバックに感謝する。
Gradle
Kotlin Gradleプラグインの更新は、新しいGradleの機能と最新のGradleバージョンとの互換性にフォーカスされている。
Kotlin 1.7.20には、Gradle 7.1のサポートが含まれている。非推奨メソッドとプロパティは削除あるいは置換され、Kotlin Gradleプログインによって生成される多くの非推奨警告が削減され、Gradle 8.0のための未来のサポートのブロックを解除している。
しかし、いくつか潜在的に互換性を破る変更があり、注意が必要かもしれない。
Target設定
-
org.jetbrains.kotlin.gradle.dsl.SingleTargetExtension
は現在SingleTargetExtension<T : KotlinTarget>
ジェネリックパラメータを持つ。 -
kotlin.targets.fromPreset()
規約は非推奨になった。代わりにkotlin.targets { fromPreset() }
がまだ使ええるが、よりターゲットを作ることに特化した方法を使うことを推奨している。 - Gradleによって自動生成されたTargetアクセサは、
kotlin.targets { }
ブロック内では使用できなくなっている。代わりにfindByName("targetName")
メソッドを使っていただきたい。
注意すべきは、そのようなアクセサは例えばkotlin.targets.linux
といった、kotlin.targets
のケース内ではまだ利用可能である。
ソースディレクトリ設定
Kotlin Gradleプラグインは現在、JavaのSourceSet
グループに対するkotlin
拡張としてKotlinのSourceDirectorySet
が追加されている。これにより、JavaやGroovyやScalaで設定する方法と同様に、build.gradle.kts
内でソースディレクトリを設定することができる。
sourceSets {
main {
kotlin {
java.setSrcDirs(listOf("src/java"))
kotlin.setSrcDirs(listOf("src/kotlin"))
}
}
}
もはや非推奨のGradle規約を使い、Kotlinのソースディレクトリを指定する必要はない。
KotlinSourceSet
にアクセスするためにkotlin
拡張を使うこともできることを覚えておいてほしい。
kotlin {
sourceSets {
main {
// …
}
}
}
JVMツールチェーン設定のための新しいメソッド
このリリースでは、JVMツールチェーン機能を有効にするため、新しいjvmToolChain()
メソッドが提供されている。implementation
やvendor
といった、追加の設定フィールドが不要な場合、Kotlin拡張からこのメソッドを使うことができる。
kotlin {
jvmToolchain(17)
}
これは追加の設定なしで、Kotlinのプロジェクトのセットアップを簡単にする。このリリースの前は、次の方法でJDKバージョンのみを指定することができた。
kotlin {
jvmToolchain {
languageVersion.set(JavaLanguageVersion.of(17))
}
}
標準ライブラリ
Kotlin 1.7.20では、java.nio.file.Path
クラスのための新しい拡張関数が提供されており、それにより、ファイルツリーを歩いて通ることができる。
-
walk()
は、指定したパスをルートとするファイルツリーを遅延して横断する。 -
fileVisitor()
は、別々にFileVisitor
を作ることができる。FileVisitor
はファイルやディレクトリを横断するときのアクションを定義する。 -
visitFileTree(fileVisitor: FileVisitor, ...)
は、準備したFileVisitor
を消費し、内部でjava.nio.file.Files.walkFileTree()
を使う。 -
visitFileTree(..., builderAction: FileVisitorBuilder.() -> Unit)
は、VisitorAction
を持つFileVisitor
を作り、visitFileTree(fileVisitor, ...)
関数を呼び出す。 -
FileVisitorResult
はFileVisitor
型の返却し、ファイルの処理を継続する、CONTINUE
デフォルト値を持つ。
java.nio.file.Path
クラスのための新しい拡張関数は、実験的機能である。いつでも削除されたり、変更されるかもしれない。オプトインが必要(詳細は下記)であり、評価目的のためにのみ使うべきである。
こちらにこれらの新しい関数で処理することができるものがいくつかある。
- 明示的に
FileVisitor
を作り、その後使う
val cleanVisitor = fileVisitor {
onPreVisitDirectory { directory, attributes ->
// ここでディレクトリ訪問時のロジック
FileVisitResult.CONTINUE
}
onVisitFile { file, attributes ->
// ここでファイル訪問時のロジック
FileVisitResult.CONTINUE
}
}
// ここにロジックが可能
projectDirectory.visitFileTree(cleanVisitor)
-
builderAction
を持つFileVisitor
を作り、すぐに使う
projectDirectory.visitFileTree {
// builderActionの定義
onPreVisitDirectory { directory, attributes ->
//ここでディレクトリ訪問時のロジック
FileVisitResult.CONTINUE
}
onVisitFile { file, attributes ->
// ここでファイル訪問時のロジック
FileVisitResult.CONTINUE
}
}
-
walk()
関数で指定したパスをルートとするファイルツリーを横断
@OptIn(kotlin.io.path.ExperimentalPathApi::class)
fun taverseFileTree() {
val cleanVisitor = fileVisitor {
onPreVisitDirectory { directory, _ ->
if (directory.name == "build") {
directory.toFile().deleteRecursively()
FileVisitResult.SKIP_SUBTREE
} else {
FileVisitResult.CONTINUE
}
}
onVisitFile { file, _ ->
if (file.extension == "class") {
file.deleteExisting()
}
FileVisitResult.CONTINUE
}
}
val rootDirectory = createTempDirectory("Project")
rootDirectory.resolve("src").let { srcDirectory ->
srcDirectory.createDirectory()
srcDirectory.resolve("A.kt").createFile()
srcDirectory.resolve("A.class").createFile()
}
rootDirectory.resolve("build").let { buildDirectory ->
buildDirectory.createDirectory()
buildDirectory.resolve("Project.jar").createFile()
}
// walk関数を使う
val directoryStructure = rootDirectory.walk(PathWalkOption.INCLUDE_DIRECTORIES)
.map { it.relativeTo(rootDirectory).toString() }
.toList().sorted()
assertPrints(directoryStructure, "[, build, build/Project.jar, src, src/A.class, src/A.kt]")
rootDirectory.visitFileTree(cleanVisitor)
val directoryStructureAfterClean = rootDirectory.walk(PathWalkOption.INCLUDE_DIRECTORIES)
.map { it.relativeTo(rootDirectory).toString() }
.toList().sorted()
assertPrints(directoryStructureAfterClean, "[, src, src/A.kt]")
}
実験的APIの通常通り、新しい拡張関数には次のオプトインが必要である。
@OptIn(kotlin.io.path.ExperimentalPathApi::class)
または@kotlin.io.path.ExperimentalPathApi
あるいは、-opt-in=kotlin.io.path.ExperimentalPathApi
コンパイラオプションが使用できる。
YouTrackで、walk()
関数と、visit拡張関数についてのフィードバックに感謝する。
ドキュメント更新
以前のリリースから、Kotlinドキュメントにはいくつかの注目すべき変更がある。
改定と改善されたページ
- 基本型の概要 - Kotlinで使われる基本的な型を学習する。数値、ブーリアン、文字、文字列、配列、符号なし整数
- Kotlin開発のためのIDE - Kotlinが公式にサポートするIDEの一覧と、コミュニティがサポートするツールを参照
Kotlinマルチプラットフォームジャーナルの新しい記事
- Nativeクロスプラットフォーム開発:選択方法? - クロスプラットフォーム開発とネイティブアプローチの概要と利点をチェック
- 6大クロスプラットフォームアプリケーション開発フレームワーク - クロスプラットフォームプロジェクトのための正しいフレームワークを選ぶのに役立つ重要な側面について読む
新規、更新チュートリアル
- Kotlin Multiplatform Mobileを始める - Kotlinでクロスプラットフォームのモバイル開発について学び、AndroidとiOS両方で動作するアプリを作る。
- Kotlin MultiplatformでフルスタックWebアプリを作る - Kotlin/JVMサーバーとKotlin/JS webクライアントで、全スタックを通じてKotlinを使ってアプリを作る。
- ReactとKotlin/JSでwebアプリケーションを作る - KotlinのDSLと典型的なReactプログラムの機能を探検するブラウザアプリを作る。
リリースドキュメントの変更
我々はもはやそれぞれのリリースのために推奨するkotlinxのライブラリの一覧を提供していない。このリストには、Kotlin自体で推奨され、テストされたバージョンのみが含まれていた。それは、いくつかのライブラリが、お互いに依存し、推奨されるKotlinバージョンと異なる特定のkotlinxバージョンを必要とすることが考慮されていなかった。
我々はライブラリがどのように相互に関連し、お互いに依存するかについての情報提供の方法を見つけることに取り組んでいる。それにより、プロジェクトでKotlinバージョンをアップグレードするときに使うべきkotlinxライブラリのバージョンが明らかになるだろう。
Kotlin 1.7.20のインストール
IntelliJ IDEA 2021.3と2022.1と2022.2は自動的にKotlinプラグインを1.7.20への更新を提案する。
Android Studio Dolphin (213), Electric Eel (221), and Flamingo (222)用にKotlinプラグイン1.7.20が今後のAndroid Studioの更新とともに配布されるだろう。
新しいコマンドラインコンパイラーが、GitHubリリースページで利用可能である。
Kotlin 1.7.20互換性ガイド
Kotlin 1.7.20は差分リリースであるが、Kotlin 1.7.0で導入された問題の拡大を制限するために行った互換性の無い変更がすでにある。
そのような変更の詳細な一覧がKotlin 1.7.20互換性ガイドで見つけることができる。