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

Kotlin 1.5.0 の新機能

リリース: 2021年5月5日

Kotlin 1.5.0では、新しい言語機能、安定版のIRベースのJVMコンパイラバックエンド、パフォーマンスの改善、そして実験的機能の安定化や旧式の機能の廃止など、進化的な変更が導入されています。

変更点の概要は、リリースブログ記事でも確認できます。

言語機能

Kotlin 1.5.0では、1.4.30のプレビューで紹介された新しい言語機能の安定版が導入されました。

これらの機能の詳細な説明は、こちらのブログ記事とKotlinドキュメントの対応するページで確認できます。

JVM records のサポート

Javaは急速に進化しており、KotlinがJavaとの相互運用性を維持できるように、最新機能の1つであるrecord classesのサポートを導入しました。

KotlinのJVM recordsのサポートには、双方向の相互運用性が含まれます。

  • Kotlinコードでは、Javaのrecord classesをプロパティを持つ通常のクラスのように使用できます。
  • KotlinクラスをJavaコードでrecordとして使用するには、dataクラスにして@JvmRecordアノテーションを付けます。
@JvmRecord
data class User(val name: String, val age: Int)

KotlinでのJVM recordsの使用に関する詳細はこちら

Sealed interfaces

Kotlinのインターフェースは、sealed修飾子を持つことができるようになりました。これは、クラスの場合と同じようにインターフェースで機能します。sealed interfaceのすべての実装は、コンパイル時に認識されます。

sealed interface Polygon

たとえば、この事実を利用して、網羅的なwhen式を記述できます。

fun draw(polygon: Polygon) = when (polygon) {
is Rectangle `->` // ...
is Triangle `->` // ...
// else は不要です - 考えられるすべての実装が網羅されています
}

さらに、sealed interfacesを使用すると、クラスは複数のsealed interfaceを直接継承できるため、より柔軟な制限付きクラス階層が可能になります。

class FilledRectangle: Polygon, Fillable

sealed interfacesの詳細はこちら

パッケージ全体の sealed class 階層

Sealed classesは、同じコンパイルユニットおよび同じパッケージのすべてのファイルにサブクラスを持つことができるようになりました。以前は、すべてのサブクラスが同じファイルに存在する必要がありました。

直接のサブクラスは、トップレベルにすることも、他の名前付きクラス、名前付きインターフェース、または名前付きオブジェクト内にネストすることもできます。

Sealed classのサブクラスは、適切に修飾された名前を持っている必要があります。ローカルオブジェクトまたは匿名オブジェクトにすることはできません。

sealed class 階層の詳細はこちら

Inline classes

Inline classesは、値を保持するだけのvalue-basedクラスのサブセットです。メモリ割り当ての使用による追加のオーバーヘッドなしに、特定の型の値のラッパーとして使用できます。

Inline classesは、クラス名の前にvalue修飾子を付けて宣言できます。

value class Password(val s: String)

JVMバックエンドでは、特別な@JvmInlineアノテーションも必要です。

@JvmInline
value class Password(val s: String)

inline修飾子は、現在警告付きで非推奨となっています。

Inline classesの詳細はこちら

Kotlin/JVM

Kotlin/JVMは、内部的にもユーザー向けにも、いくつかの改善が加えられています。その中で最も注目すべき点は次のとおりです。

安定版のJVM IRバックエンド

Kotlin/JVMコンパイラー用のIRベースのバックエンド安定版になり、デフォルトで有効になっています。

Kotlin 1.4.0以降、IRベースのバックエンドの初期バージョンがプレビューで利用可能になり、言語バージョン1.5のデフォルトになりました。古いバックエンドは、以前の言語バージョンでは引き続きデフォルトで使用されます。

IRバックエンドの利点とその将来の開発の詳細については、こちらのブログ記事をご覧ください。

Kotlin 1.5.0で古いバックエンドを使用する必要がある場合は、プロジェクトの構成ファイルに次の行を追加できます。

  • Gradleの場合:
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile> {
kotlinOptions.useOldBackend = true
}
  • Mavenの場合:
<configuration>
<args>
<arg>-Xuse-old-backend</arg>
</args>
</configuration>

新しいデフォルトのJVMターゲット: 1.8

Kotlin/JVMコンパイルのデフォルトのターゲットバージョンは、1.8になりました。1.6ターゲットは非推奨になりました。

JVM 1.6用のビルドが必要な場合は、このターゲットに切り替えることができます。方法はこちらをご覧ください。

invokedynamicを介したSAMアダプター

Kotlin 1.5.0では、SAM (Single Abstract Method) 変換のコンパイルに動的呼び出し (invokedynamic) が使用されるようになりました。

新しい実装では、LambdaMetafactory.metafactory()が使用され、コンパイル中に補助ラッパークラスが生成されなくなりました。これにより、アプリケーションのJARのサイズが縮小され、JVMの起動パフォーマンスが向上します。

匿名クラス生成に基づく古い実装スキームにロールバックするには、コンパイラーオプション-Xsam-conversions=classを追加します。

GradleMaven、およびコマンドラインコンパイラーでコンパイラーオプションを追加する方法をご覧ください。

invokedynamicを介したラムダ

注記

プレーンなKotlinラムダを invokedynamic にコンパイルすることは、実験的です。これはいつでも削除または変更される可能性があります。 オプトインが必要です(詳細は以下を参照)、評価目的でのみ使用する必要があります。YouTrackでフィードバックをお待ちしております。

Kotlin 1.5.0では、プレーンなKotlinラムダ(関数型インターフェースのインスタンスに変換されない)を動的呼び出し (invokedynamic) にコンパイルするための実験的サポートが導入されています。この実装では、LambdaMetafactory.metafactory()を使用して、より軽量なバイナリを生成します。これにより、必要なクラスが実行時に効果的に生成されます。現在、通常のラムダコンパイルと比較して、3つの制限があります。

  • invokedynamicにコンパイルされたラムダはシリアライズできません。
  • このようなラムダでtoString()を呼び出すと、読みやすい文字列表現が生成されません。
  • 実験的なreflect APIは、LambdaMetafactoryで作成されたラムダをサポートしていません。

この機能を試すには、-Xlambdas=indyコンパイラーオプションを追加します。このYouTrackチケットを使用してフィードバックをお寄せいただければ幸いです。

GradleMaven、およびコマンドラインコンパイラーでコンパイラーオプションを追加する方法をご覧ください。

@JvmDefaultおよび古いXjvm-defaultモードの非推奨

Kotlin 1.4.0より前には、@JvmDefaultアノテーションと-Xjvm-default=enableおよび-Xjvm-default=compatibilityモードがありました。これらは、Kotlinインターフェースの特定の非抽象メンバーに対してJVMデフォルトメソッドを作成するために使用されました。

Kotlin 1.4.0では、新しいXjvm-defaultモードを導入しました。これにより、プロジェクト全体のデフォルトメソッドの生成がオンになります。

Kotlin 1.5.0では、@JvmDefaultと古いXjvm-defaultモード: -Xjvm-default=enableおよび-Xjvm-default=compatibilityを非推奨にしています。

Java interopのデフォルトメソッドの詳細はこちら

nullabilityアノテーションの処理の改善

Kotlinは、nullabilityアノテーションを使用してJavaからの型のnullability情報を処理することをサポートしています。Kotlin 1.5.0では、この機能に対していくつかの改善が導入されています。

  • 依存関係として使用されるコンパイル済みのJavaライブラリの型引数に関するnullabilityアノテーションを読み取ります。
  • 次のターゲットに対してTYPE_USEターゲットを持つnullabilityアノテーションをサポートします。
    • 配列
    • Varargs
    • フィールド
    • 型パラメーターとその境界
    • 基底クラスとインターフェースの型引数
  • nullabilityアノテーションに型に適用可能な複数のターゲットがあり、これらのターゲットの1つがTYPE_USEである場合、TYPE_USEが優先されます。 たとえば、メソッドシグネチャ@Nullable String[] f()は、@NullableTYPE_USEMETHODの両方をターゲットとしてサポートする場合、fun f(): Array<String?>!になります。

これらの新しくサポートされるケースでは、KotlinからJavaを呼び出すときに誤った型のnullabilityを使用すると、警告が生成されます。-Xtype-enhancement-improvements-strict-modeコンパイラーオプションを使用して、これらのケースの厳密モード(エラー報告付き)を有効にします。

null-safetyとプラットフォーム型の詳細はこちら

Kotlin/Native

Kotlin/Nativeは、より高性能で安定しています。注目すべき変更点は次のとおりです。

パフォーマンスの改善

1.5.0では、Kotlin/Nativeは、コンパイルと実行の両方を高速化する一連のパフォーマンス改善を受けています。

コンパイラーキャッシュが、linuxX64(Linuxホストのみ)およびiosArm64ターゲットのデバッグモードでサポートされるようになりました。コンパイラーキャッシュを有効にすると、最初のコンパイルを除き、ほとんどのデバッグコンパイルがはるかに高速に完了します。測定では、テストプロジェクトで約200%の速度向上が見られました。

新しいターゲットでコンパイラーキャッシュを使用するには、プロジェクトのgradle.propertiesに次の行を追加してオプトインします。

  • linuxX64の場合: kotlin.native.cacheKind.linuxX64=static
  • iosArm64の場合: kotlin.native.cacheKind.iosArm64=static

コンパイラーキャッシュを有効にした後に問題が発生した場合は、YouTrackの問題追跡ツールに報告してください。

その他の改善により、Kotlin/Nativeコードの実行が高速化されます。

  • トリビアルなプロパティアクセサーはインライン化されます。
  • 文字列リテラルに対するtrimIndent()は、コンパイル中に評価されます。

メモリーリークチェッカーの無効化

組み込みのKotlin/Nativeメモリーリークチェッカーは、デフォルトで無効になっています。

これは当初、内部で使用するために設計されており、限られた数のケースでしかリークを見つけることができませんでした。さらに、アプリケーションのクラッシュを引き起こす可能性のある問題があることが後で判明しました。そのため、メモリーリークチェッカーをオフにすることにしました。

メモリーリークチェッカーは、特定のケース(ユニットテストなど)では引き続き役立つ可能性があります。これらのケースでは、次のコード行を追加して有効にすることができます。

Platform.isMemoryLeakCheckerActive = true

アプリケーションのランタイムでチェッカーを有効にすることはお勧めしません。

Kotlin/JS

Kotlin/JSは、1.5.0で進化的な変更を受けています。JS IRコンパイラーバックエンドを安定化させ、他のアップデートを出荷する作業を継続しています。

webpackのバージョン5へのアップグレード

Kotlin/JS Gradleプラグインは、webpack 4の代わりにwebpack 5をブラウザターゲットで使用するようになりました。これはwebpackのメジャーアップグレードであり、互換性のない変更が加えられています。カスタムwebpack構成を使用している場合は、webpack 5のリリースノートを確認してください。

webpackを使用したKotlin/JSプロジェクトのバンドルの詳細はこちら

IRコンパイラー用のフレームワークとライブラリ

Kotlin/JS IRコンパイラーはAlpha段階です。互換性のない変更が必要になる場合があり、将来手動での移行が必要になる場合があります。YouTrackでフィードバックをお待ちしております。

Kotlin/JSコンパイラー用のIRベースのバックエンドの開発とともに、ライブラリ作成者がbothモードでプロジェクトを構築することを推奨し、支援しています。これは、両方のKotlin/JSコンパイラー用の成果物を生成できることを意味し、新しいコンパイラーのエコシステムを成長させます。

多くの有名なフレームワークとライブラリがすでにIRバックエンドで利用可能です: KVisionfritz2doodleなど。プロジェクトで使用している場合は、IRバックエンドでビルドして、そのメリットを確認できます。

独自のライブラリを作成する場合は、「both」モードでコンパイルします。これにより、クライアントも新しいコンパイラーで使用できるようになります。

Kotlin Multiplatform

Kotlin 1.5.0では、プラットフォームごとのテスト依存関係の選択が簡素化されました。これは、Gradleプラグインによって自動的に行われるようになりました。

charカテゴリーを取得するための新しいAPIが、マルチプラットフォームプロジェクトで利用できるようになりました

標準ライブラリ

標準ライブラリは、実験的な部分の安定化から新機能の追加まで、さまざまな変更と改善を受けています。

標準ライブラリの変更の詳細については、こちらのブログ記事をご覧ください。

安定版の符号なし整数型

UIntULongUByteUShort符号なし整数型が安定版になりました。これらの型、範囲、およびその進捗状況に対する操作も同様です。符号なし配列とその操作は、ベータ版のままです。

符号なし整数型の詳細はこちら

大文字/小文字テキストの安定版ロケール非依存API

このリリースでは、大文字/小文字のテキスト変換用の新しいロケール非依存APIが提供されます。これは、ロケール依存のtoLowerCase()toUpperCase()capitalize()、およびdecapitalize() API関数の代替手段を提供します。新しいAPIは、さまざまなロケール設定によるエラーを回避するのに役立ちます。

Kotlin 1.5.0は、完全に安定版の代替手段を以下に示します。

  • String関数の場合:

    以前のバージョン1.5.0の代替手段
    String.toUpperCase()String.uppercase()
    String.toLowerCase()String.lowercase()
    String.capitalize()String.replaceFirstChar { it.uppercase() }
    String.decapitalize()String.replaceFirstChar { it.lowercase() }
  • Char関数の場合:

    以前のバージョン1.5.0の代替手段
    Char.toUpperCase()Char.uppercaseChar(): Char
    Char.uppercase(): String
    Char.toLowerCase()Char.lowercaseChar(): Char
    Char.lowercase(): String
    Char.toTitleCase()Char.titlecaseChar(): Char
    Char.titlecase(): String

Kotlin/JVMの場合、明示的なLocaleパラメーターを持つオーバーロードされたuppercase()lowercase()、およびtitlecase()関数もあります。

古いAPI関数は非推奨としてマークされており、今後のリリースで削除されます。

テキスト処理関数の変更の完全なリストについては、KEEPを参照してください。

安定版のchar-to-integer変換API

Kotlin 1.5.0以降、新しいchar-to-codeおよびchar-to-digit変換関数は安定版です。これらの関数は、類似のstring-to-Int変換と混同されることが多かった現在のAPI関数を置き換えます。

新しいAPIは、この名前の混乱を取り除き、コードの動作をより透過的かつ明確にします。

このリリースでは、次の明確に名前が付けられた関数セットに分割されたChar変換が導入されています。

  • Charの整数コードを取得し、指定されたコードからCharを構築する関数:
fun Char(code: Int): Char
fun Char(code: UShort): Char
val Char.code: Int
  • Charをそれが表す数字の数値に変換する関数:
fun Char.digitToInt(radix: Int): Int
fun Char.digitToIntOrNull(radix: Int): Int?
  • それが表す負でない1桁の数字を対応するChar表現に変換するIntの拡張関数:
fun Int.digitToChar(radix: Int): Char

Number.toChar()とその実装(Int.toChar()を除くすべて)や、数値型への変換のためのChar拡張機能(Char.toInt()など)を含む古い変換APIは、非推奨になりました。

char-to-integer変換APIの詳細については、KEEPを参照してください

安定版のPath API

java.nio.file.Pathの拡張機能を含む実験的なPath API安定版になりました。

// div (/)演算子を使用してパスを構築する
val baseDir = Path("/base")
val subDir = baseDir / "subdirectory"

// ディレクトリ内のファイルを一覧表示する
val kotlinFiles: List<Path> = Path("/home/user").listDirectoryEntries("*.kt")

Path APIの詳細はこちら

フロア除算とmod演算子

モジュラ演算用の新しい操作が標準ライブラリに追加されました。

  • floorDiv()は、フロア除算の結果を返します。これは、整数型で使用できます。
  • mod()は、フロア除算の剰余(モジュラス)を返します。これは、すべての数値型で使用できます。

これらの操作は、既存の整数の除算およびrem()関数(または%演算子)と非常によく似ていますが、負の数では動作が異なります。

  • a.floorDiv(b)は、floorDivが結果を切り下げる(小さい整数に向かって)点で通常の/と異なりますが、/は結果を0に近い整数に切り捨てます。
  • a.mod(b)は、aa.floorDiv(b) * bの差です。これはゼロであるか、bと同じ符号を持つかのいずれかですが、a % bは異なる符号を持つ可能性があります。
fun main() {

println("フロア除算 -5/3: ${(-5).floorDiv(3)}")
println( "モジュラス: ${(-5).mod(3)}")

println("切り捨てられた除算 -5/3: ${-5 / 3}")
println( "剰余: ${-5 % 3}")

}

Duration APIの変更

注意

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

異なる時間単位で期間量を表すための実験的なDurationクラスがあります。1.5.0では、Duration APIに次の変更が加えられました。

  • 内部値の表現では、より高い精度を提供するために、DoubleではなくLongを使用するようになりました。
  • Longの特定の時間単位への変換のための新しいAPIがあります。これは、Double値で動作し、非推奨になった古いAPIを置き換えるために提供されます。たとえば、Duration.inWholeMinutesは、Longとして表される期間の値を返し、Duration.inMinutesを置き換えます。
  • 数値からDurationを構築するための新しいコンパニオン関数があります。たとえば、Duration.seconds(Int)は、整数の秒数を表すDurationオブジェクトを作成します。Int.secondsのような古い拡張プロパティは非推奨になりました。
import kotlin.time.Duration
import kotlin.time.ExperimentalTime

@ExperimentalTime
fun main() {

val duration = Duration.milliseconds(120000)
println("There are ${duration.inWholeSeconds} seconds in ${duration.inWholeMinutes} minutes")

}

charカテゴリーを取得するための新しいAPIが、マルチプラットフォームコードで利用できるようになりました

Kotlin 1.5.0では、マルチプラットフォームプロジェクトでUnicodeに従って文字のカテゴリーを取得するための新しいAPIが導入されました。いくつかの関数がすべてのプラットフォームと共通コードで利用できるようになりました。

文字が文字または数字であるかどうかを確認する関数:

fun main() {

val chars = listOf('a', '1', '+')
val (letterOrDigitList, notLetterOrDigitList) = chars.partition { it.isLetterOrDigit() }
println(letterOrDigitList) // [a, 1]
println(notLetterOrDigitList) // [+]

}

文字の大文字と小文字を確認する関数:

fun main() {

val chars = listOf('Dž', 'Lj', 'Nj', 'Dz', '1', 'A', 'a', '+')
val (titleCases, notTitleCases) = chars.partition { it.isTitleCase() }
println(titleCases) // [Dž, Lj, Nj, Dz]
println(notTitleCases) // [1, A, a, +]

}

その他の関数:

プロパティChar.categoryとその戻り値の型 enum class CharCategory。これは、Unicodeに従って文字の一般的なカテゴリーを示します。これも、マルチプラットフォームプロジェクトで利用できるようになりました。

文字の詳細はこちら

新しいコレクション関数firstNotNullOf()

新しいfirstNotNullOf()およびfirstNotNullOfOrNull() 関数は、mapNotNull()first()またはfirstOrNull()と組み合わせます。 カスタムセレクター関数を使用して元のコレクションをマップし、最初のnull以外の値を返します。そのような値がない場合、 firstNotNullOf()は例外をスローし、firstNotNullOfOrNull()はnullを返します。

fun main() {

val data = listOf("Kotlin", "1.5")
println(data.firstNotNullOf(String::toDoubleOrNull))
println(data.firstNotNullOfOrNull(String::toIntOrNull))

}

String?.toBoolean()の厳密バージョン

2つの新しい関数は、既存のString?.toBoolean()の大文字と小文字を区別する厳密なバージョンを導入します。

fun main() {

println("true".toBooleanStrict())
println("1".toBooleanStrictOrNull())
// println("1".toBooleanStrict()) // 例外

}

kotlin-test ライブラリ

kotlin-testライブラリには、いくつかの新機能が導入されています。

マルチプラットフォームプロジェクトでのテスト依存関係の簡素化された使用法

kotlin-test依存関係を使用してcommonTestソースセットにテスト用の依存関係を追加できるようになりました。 Gradleプラグインは、各テストソースセットの対応するプラットフォーム依存関係を推測します。

さらに、共有またはプラットフォーム固有のソースセットでkotlin-test依存関係を使用できます。

明示的な依存関係を持つ既存のkotlin-testセットアップは、GradleとMavenの両方で引き続き機能します。

テストライブラリへの依存関係の設定の詳細をご覧ください。

Kotlin/JVMソースセットのテストフレームワークの自動選択

Gradleプラグインは、テストフレームワークを自動的に選択して依存関係を追加するようになりました。必要なのは、共通ソースセットに依存関係kotlin-testを追加することだけです。

GradleはデフォルトでJUnit 4を使用します。したがって、kotlin("test")依存関係はJUnit 4のバリアント、つまりkotlin-test-junitに解決されます。

kotlin {
sourceSets {
val commonTest by getting {
dependencies {
implementation(kotlin("test")) // これにより、依存関係がもたらされます
// JUnit 4へのトラン