Skip to content

Kotlin 1.4.0の新機能

リリース日: 2020年8月17日

Kotlin 1.4.0では、すべてのコンポーネントに多くの改善が施されており、品質とパフォーマンスに重点が置かれています。 以下に、Kotlin 1.4.0における最も重要な変更点のリストを記載します。

言語機能と改善

Kotlin 1.4.0には、さまざまな言語機能と改善が含まれています。これには以下が含まれます。

KotlinインターフェースのSAM変換

Kotlin 1.4.0より前は、SAM (Single Abstract Method) 変換はKotlinからJavaメソッドおよびJavaインターフェースを扱う場合にのみ適用できました。これからは、Kotlinインターフェースに対してもSAM変換を使用できます。 これを行うには、fun修飾子を使用してKotlinインターフェースを明示的に関数型としてマークします。

SAM変換は、単一の抽象メソッドのみを持つインターフェースがパラメータとして期待されるときにラムダを引数として渡す場合に適用されます。 この場合、コンパイラはラムダを抽象メンバー関数を実装するクラスのインスタンスに自動的に変換します。

kotlin
fun interface IntPredicate {
    fun accept(i: Int): Boolean
}

val isEven = IntPredicate { it % 2 == 0 }

fun main() { 
    println("Is 7 even? - ${isEven.accept(7)}")
}

Kotlinの関数型インターフェースとSAM変換について詳しく学ぶ

ライブラリ作者のためのExplicit APIモード

Kotlinコンパイラは、ライブラリ作者向けに_Explicit APIモード_を提供します。このモードでは、コンパイラは、 ライブラリのAPIをより明確で一貫性のあるものにするのに役立つ追加のチェックを実行します。 これにより、ライブラリのパブリックAPIに公開される宣言に以下の要件が追加されます。

  • デフォルトの可視性でパブリックAPIに公開される宣言には、可視性修飾子が必要です。 これにより、意図せずに宣言がパブリックAPIに公開されることがなくなります。
  • パブリックAPIに公開されるプロパティおよび関数には、明示的な型指定が必要です。 これにより、APIユーザーが使用するAPIメンバーの型を認識できるようにします。

設定によっては、これらの明示的なAPIはエラー(_strict_モード)または警告(_warning_モード)を生成する場合があります。 読みやすさと常識のために、特定の種類の宣言はこれらのチェックから除外されます。

  • プライマリコンストラクタ
  • データクラスのプロパティ
  • プロパティゲッターとセッター
  • overrideメソッド

Explicit APIモードは、モジュールのプロダクションソースのみを分析します。

モジュールをExplicit APIモードでコンパイルするには、以下の行をGradleビルドスクリプトに追加します。

kotlin
kotlin {    
    // for strict mode
    explicitApi() 
    // or
    explicitApi = ExplicitApiMode.Strict
    
    // for warning mode
    explicitApiWarning()
    // or
    explicitApi = ExplicitApiMode.Warning
}
groovy
kotlin {    
    // for strict mode
    explicitApi() 
    // or
    explicitApi = 'strict'
    
    // for warning mode
    explicitApiWarning()
    // or
    explicitApi = 'warning'
}

コマンドラインコンパイラを使用する場合は、-Xexplicit-apiコンパイラオプションにstrictまたはwarningの値を設定してExplicit APIモードに切り替えます。

bash
-Xexplicit-api={strict|warning}

Explicit APIモードの詳細については、KEEPを参照してください

名前付き引数と位置指定引数の混在

Kotlin 1.3では、名前付き引数で関数を呼び出す場合、名前のないすべての引数(位置指定引数)を最初の名前付き引数の前に配置する必要がありました。例えば、f(1, y = 2)は呼び出せましたが、f(x = 1, 2)は呼び出せませんでした。

すべての引数が正しい位置にありながら、途中の1つの引数に名前を指定したい場合に、この制限は非常に煩わしいものでした。特に、Booleanまたはnullの値がどの属性に属するかを明確にするのに役立ちました。

Kotlin 1.4では、このような制限はありません。位置指定引数のセットの途中の引数に名前を指定できるようになりました。さらに、正しい順序を保つ限り、位置指定引数と名前付き引数を好きなように混在させることができます。

kotlin
fun reformat(
    str: String,
    uppercaseFirstLetter: Boolean = true,
    wordSeparator: Char = ' '
) {
    // ...
}

//Function call with a named argument in the middle
reformat("This is a String!", uppercaseFirstLetter = false , '-')

末尾のカンマ

Kotlin 1.4では、引数リストやパラメータリスト、whenのエントリ、分割宣言のコンポーネントなどの列挙において、末尾にカンマを追加できるようになりました。 末尾にカンマを追加することで、カンマを追加したり削除したりすることなく、新しい項目を追加したり順序を変更したりできます。

これは、パラメータや値に複数行構文を使用する場合に特に役立ちます。末尾にカンマを追加した後、パラメータや値の行を簡単に交換できます。

kotlin
fun reformat(
    str: String,
    uppercaseFirstLetter: Boolean = true,
    wordSeparator: Character = ' ', //trailing comma
) {
    // ...
}
kotlin
val colors = listOf(
    "red",
    "green",
    "blue", //trailing comma
)

呼び出し可能参照の改善

Kotlin 1.4では、呼び出し可能参照のより多くのケースをサポートしています。

  • デフォルト引数値を持つ関数への参照
  • Unitを返す関数での関数参照
  • 関数の引数の数に基づいて適応する参照
  • 呼び出し可能参照でのsuspend変換

デフォルト引数値を持つ関数への参照

デフォルト引数値を持つ関数への呼び出し可能参照を使用できるようになりました。関数fooへの呼び出し可能参照が引数を持たない場合、デフォルト値0が使用されます。

kotlin
fun foo(i: Int = 0): String = "$i!"

fun apply(func: () -> String): String = func()

fun main() {
    println(apply(::foo))
}

以前は、デフォルト引数値を使用するために、関数applyに追加のオーバーロードを記述する必要がありました。

kotlin
// some new overload
fun applyInt(func: (Int) -> String): String = func(0)

Unitを返す関数での関数参照

Kotlin 1.4では、Unitを返す関数で、任意の型を返す関数への呼び出し可能参照を使用できます。 Kotlin 1.4より前は、この場合ラムダ引数しか使用できませんでした。現在は、ラムダ引数と呼び出し可能参照の両方を使用できます。

kotlin
fun foo(f: () -> Unit) { }
fun returnsInt(): Int = 42

fun main() {
    foo { returnsInt() } // this was the only way to do it  before 1.4
    foo(::returnsInt) // starting from 1.4, this also works
}

関数の引数の数に基づいて適応する参照

可変数の引数(vararg)を渡すときに、関数への呼び出し可能参照を適応させることができるようになりました。 渡された引数リストの最後に、同じ型の任意の数のパラメータを渡すことができます。

kotlin
fun foo(x: Int, vararg y: String) {}

fun use0(f: (Int) -> Unit) {}
fun use1(f: (Int, String) -> Unit) {}
fun use2(f: (Int, String, String) -> Unit) {}

fun test() {
    use0(::foo) 
    use1(::foo) 
    use2(::foo) 
}

呼び出し可能参照でのsuspend変換

ラムダに対するsuspend変換に加え、Kotlinはバージョン1.4.0から呼び出し可能参照に対するsuspend変換もサポートするようになりました。

kotlin
fun call() {}
fun takeSuspend(f: suspend () -> Unit) {}

fun test() {
    takeSuspend { call() } // OK before 1.4
    takeSuspend(::call) // In Kotlin 1.4, it also works
}

ループ内のwhen式でのbreakとcontinueの使用

Kotlin 1.3では、ループ内に含まれるwhen式内で非修飾のbreakcontinueを使用することはできませんでした。その理由は、これらのキーワードがwhen式でのフォールスルーの振る舞いのために予約されていたためです。

そのため、ループ内のwhen式でbreakcontinueを使用したい場合は、それらをラベル付けする必要があり、これはかなり面倒でした。

kotlin
fun test(xs: List<Int>) {
    LOOP@for (x in xs) {
        when (x) {
            2 -> continue@LOOP
            17 -> break@LOOP
            else -> println(x)
        }
    }
}

Kotlin 1.4では、ループ内に含まれるwhen式内でラベルなしでbreakcontinueを使用できます。これらは、最も近い囲むループを終了するか、その次のステップに進むという期待通りの動作をします。

kotlin
fun test(xs: List<Int>) {
    for (x in xs) {
        when (x) {
            2 -> continue
            17 -> break
            else -> println(x)
        }
    }
}

when内のフォールスルーの振る舞いは、今後の設計の対象となります。

IDEの新機能

Kotlin 1.4では、IntelliJ IDEAの新しいツールを使用してKotlin開発を簡素化できます。

新しい柔軟なプロジェクトウィザード

柔軟な新しいKotlinプロジェクトウィザードを使用すると、UIなしでは設定が難しいマルチプラットフォームプロジェクトを含む、さまざまな種類のKotlinプロジェクトを簡単に作成および設定できます。

Kotlin Project Wizard – Multiplatform project

新しいKotlinプロジェクトウィザードは、シンプルで柔軟です。

  1. やりたいことに応じて、プロジェクトテンプレートを選択します。今後、さらに多くのテンプレートが追加される予定です。
  2. ビルドシステムを選択します – Gradle(KotlinまたはGroovy DSL)、Maven、またはIntelliJ IDEA。 Kotlinプロジェクトウィザードは、選択されたプロジェクトテンプレートでサポートされているビルドシステムのみを表示します。
  3. プロジェクト構造をメイン画面で直接プレビューします

その後、プロジェクトの作成を完了するか、オプションで次の画面でプロジェクトを設定します

  1. このプロジェクトテンプレートでサポートされているモジュールとターゲットを追加/削除します
  2. モジュールとターゲットの設定を行います。例えば、ターゲットJVMバージョン、ターゲットテンプレート、テストフレームワークなどです。

Kotlin Project Wizard - Configure targets

将来的には、Kotlinプロジェクトウィザードにさらに多くの設定オプションとテンプレートを追加することで、さらに柔軟にする予定です。

以下のチュートリアルを参考に、新しいKotlinプロジェクトウィザードを試すことができます。

コルーチンデバッガー

多くの人がすでに非同期プログラミングにコルーチンを使用しています。 しかし、Kotlin 1.4以前のコルーチンでのデバッグは、非常に厄介なものでした。コルーチンがスレッド間をジャンプするため、特定のコルーチンが何をしているのかを理解し、そのコンテキストを確認することが困難でした。場合によっては、ブレークポイントを越えてステップを追跡することが単に機能しませんでした。結果として、コルーチンを使用するコードをデバッグするために、ログ記録または精神的な努力に頼る必要がありました。

Kotlin 1.4では、Kotlinプラグインに搭載された新機能により、コルーチンのデバッグがはるかに便利になりました。

NOTE

デバッグはkotlinx-coroutines-coreバージョン1.3.8以降で機能します。

デバッグツールウィンドウには、新しいCoroutinesタブが含まれています。このタブでは、現在実行中および中断中のコルーチンの両方に関する情報を見つけることができます。コルーチンは、それらが実行されているディスパッチャによってグループ化されています。

Debugging coroutines

これで、次のことが可能になります。

  • 各コルーチンの状態を簡単に確認できます。
  • 実行中および中断中のコルーチンの両方について、ローカル変数とキャプチャされた変数の値を確認できます。
  • 完全なコルーチン作成スタックと、コルーチン内の呼び出しスタックを確認できます。スタックには、標準のデバッグ中に失われる可能性のある変数値を伴うすべてのフレームが含まれています。

各コルーチンの状態とそのスタックを含む完全なレポートが必要な場合は、Coroutinesタブ内を右クリックし、Get Coroutines Dumpをクリックします。現在、コルーチンダンプはかなり単純ですが、今後のKotlinバージョンでより読みやすく、役立つものにする予定です。

Coroutines Dump

このブログ記事およびIntelliJ IDEAドキュメントでコルーチンのデバッグについて詳しく学ぶ。

新しいコンパイラ

新しいKotlinコンパイラは非常に高速になり、サポートされているすべてのプラットフォームを統合し、コンパイラ拡張のためのAPIを提供します。これは長期的なプロジェクトであり、Kotlin 1.4.0ではすでにいくつかのステップを完了しています。

新しい、より強力な型推論アルゴリズム

Kotlin 1.4は、新しい、より強力な型推論アルゴリズムを使用しています。この新しいアルゴリズムは、Kotlin 1.3ではコンパイラオプションを指定することで試すことができましたが、現在はデフォルトで使用されています。新しいアルゴリズムで修正された問題の全リストは、YouTrackで確認できます。以下に、最も顕著な改善点の一部を記載します。

型が自動的に推論されるケースの増加

新しい推論アルゴリズムは、古いアルゴリズムでは明示的に指定する必要があった多くのケースで型を推論します。 例えば、以下の例では、ラムダパラメータitの型はString?に正しく推論されます。

kotlin
val rulesMap: Map<String, (String?) -> Boolean> = mapOf(
    "weak" to { it != null },
    "medium" to { !it.isNullOrBlank() },
    "strong" to { it != null && "^[a-zA-Z0-9]+$".toRegex().matches(it) }
)

fun main() {
    println(rulesMap.getValue("weak")("abc!"))
    println(rulesMap.getValue("strong")("abc"))
    println(rulesMap.getValue("strong")("abc!"))
}

Kotlin 1.3では、これを機能させるには明示的なラムダパラメータを導入するか、toを明示的なジェネリック引数を持つPairコンストラクタに置き換える必要がありました。

ラムダの最後の式に対するスマートキャスト

Kotlin 1.3では、ラムダ内の最後の式は、期待される型を指定しない限りスマートキャストされませんでした。したがって、以下の例では、Kotlin 1.3はresult変数の型をString?として推論します。

kotlin
val result = run {
    var str = currentValue()
    if (str == null) {
        str = "test"
    }
    str // the Kotlin compiler knows that str is not null here
}
// The type of 'result' is String? in Kotlin 1.3 and String in Kotlin 1.4

Kotlin 1.4では、新しい推論アルゴリズムのおかげで、ラムダ内の最後の式がスマートキャストされ、この新しい、より正確な型が、結果となるラムダの型を推論するために使用されます。したがって、result変数の型はStringになります。

Kotlin 1.3では、このようなケースを機能させるために、明示的なキャスト(!!またはas Stringのような型キャスト)を追加する必要があることがよくありましたが、現在はこれらのキャストは不要になりました。

呼び出し可能参照に対するスマートキャスト

Kotlin 1.3では、スマートキャストされた型のメンバー参照にアクセスできませんでした。Kotlin 1.4では、それが可能になりました。

kotlin
import kotlin.reflect.KFunction

sealed class Animal
class Cat : Animal() {
    fun meow() {
        println("meow")
    }
}

class Dog : Animal() {
    fun woof() {
        println("woof")
    }
}

fun perform(animal: Animal) {
    val kFunction: KFunction<*> = when (animal) {
        is Cat -> animal::meow
        is Dog -> animal::woof
    }
    kFunction.call()
}

fun main() {
    perform(Cat())
}

animal変数が特定の型CatおよびDogにスマートキャストされた後、異なるメンバー参照animal::meowおよびanimal::woofを使用できます。型チェックの後、サブタイプに対応するメンバー参照にアクセスできます。

委譲プロパティのより良い推論

委譲プロパティの型は、byキーワードに続くデリゲート式を解析する際に考慮されませんでした。例えば、以下のコードは以前はコンパイルできませんでしたが、現在ではコンパイラがoldnewパラメータの型をString?として正しく推論します。

kotlin
import kotlin.properties.Delegates

fun main() {
    var prop: String? by Delegates.observable(null) { p, old, new ->
        println("$old$new")
    }
    prop = "abc"
    prop = "xyz"
}

引数の異なるJavaインターフェースに対するSAM変換

Kotlinは当初からJavaインターフェースのSAM変換をサポートしていましたが、既存のJavaライブラリを扱う際に時折不便だったサポートされないケースが1つありました。2つのSAMインターフェースをパラメータとして取るJavaメソッドを呼び出す場合、両方の引数はラムダまたは通常のオブジェクトのいずれかである必要がありました。一方の引数をラムダとして渡し、もう一方をオブジェクトとして渡すことはできませんでした。

新しいアルゴリズムはこの問題を修正し、どんな場合でもSAMインターフェースの代わりにラムダを渡すことができ、これは自然に期待される動作です。

java
// FILE: A.java
public class A {
    public static void foo(Runnable r1, Runnable r2) {}
}
kotlin
// FILE: test.kt
fun test(r1: Runnable) {
    A.foo(r1) {}  // Works in Kotlin 1.4
}

KotlinにおけるJava SAMインターフェース

Kotlin 1.4では、KotlinでJava SAMインターフェースを使用し、それらにSAM変換を適用できます。

kotlin
import java.lang.Runnable

fun foo(r: Runnable) {}

fun test() { 
    foo { } // OK
}

Kotlin 1.3では、SAM変換を実行するには、上記の関数fooをJavaコードで宣言する必要がありました。

統合されたバックエンドと拡張性

Kotlinには、実行可能ファイルを生成する3つのバックエンドがあります。Kotlin/JVM、Kotlin/JS、Kotlin/Nativeです。Kotlin/JVMとKotlin/JSはそれぞれ独立して開発されたため、多くのコードを共有していません。Kotlin/Nativeは、Kotlinコードの中間表現(IR)を中心に構築された新しいインフラストラクチャに基づいています。

現在、Kotlin/JVMとKotlin/JSを同じIRに移行しています。その結果、3つのバックエンドすべてが多くのロジックを共有し、統一されたパイプラインを持っています。これにより、ほとんどの機能、最適化、バグ修正をすべてのプラットフォームで一度に実装できます。どちらの新しいIRベースのバックエンドもAlpha安定性です。

共通のバックエンドインフラストラクチャは、マルチプラットフォームコンパイラ拡張の道も開きます。パイプラインに接続し、すべてのプラットフォームで自動的に機能するカスタム処理と変換を追加できるようになります。

現在Alpha版である新しいJVM IRおよびJS IRバックエンドを使用し、フィードバックを共有していただくことをお勧めします。

Kotlin/JVM

Kotlin 1.4.0には、以下のようなJVM固有の改善が多数含まれています。

新しいJVM IRバックエンド

Kotlin/JSと同様に、Kotlin/JVMも統合されたIRバックエンドに移行しています。これにより、ほとんどの機能やバグ修正をすべてのプラットフォームで一度に実装できます。また、これを利用して、すべてのプラットフォームで機能するマルチプラットフォーム拡張を作成することもできます。

Kotlin 1.4.0では、そのような拡張のための公開APIはまだ提供されていませんが、私たちはJetpack Composeを含むパートナーと密接に協力しており、彼らはすでに新しいバックエンドを使用してコンパイラプラグインを構築しています。

現在Alpha版である新しいKotlin/JVMバックエンドを試していただき、課題追跡システムに問題や機能リクエストを提出していただくことをお勧めします。これにより、コンパイラのパイプラインを統一し、Jetpack Composeのようなコンパイラ拡張をKotlinコミュニティに迅速に提供できるようになります。

新しいJVM IRバックエンドを有効にするには、Gradleビルドスクリプトで追加のコンパイラオプションを指定します。

kotlin
kotlinOptions.useIR = true

NOTE

Jetpack Composeを有効にすると、kotlinOptionsでコンパイラオプションを指定することなく、新しいJVMバックエンドに自動的にオプトインされます。

コマンドラインコンパイラを使用する場合は、-Xuse-irコンパイラオプションを追加します。

NOTE

新しいJVM IRバックエンドによってコンパイルされたコードは、新しいバックエンドを有効にした場合にのみ使用できます。そうしないと、エラーが発生します。

この点を考慮すると、ライブラリの作者が本番環境で新しいバックエンドに切り替えることはお勧めしません。

デフォルトメソッドを生成するための新しいモード

KotlinコードをJVM 1.8以降のターゲットにコンパイルする場合、Kotlinインターフェースの非抽象メソッドをJavaのdefaultメソッドにコンパイルできました。この目的のために、そのようなメソッドをマークするための@JvmDefaultアノテーションと、このアノテーションの処理を有効にする-Xjvm-defaultコンパイラオプションを含むメカニズムがありました。

1.4.0では、デフォルトメソッドを生成するための新しいモードである-Xjvm-default=allを追加しました。これは、Kotlinインターフェースのすべての非抽象メソッドをdefault Javaメソッドにコンパイルします。defaultなしでコンパイルされたインターフェースを使用するコードとの互換性のために、all-compatibilityモードも追加しました。

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

nullチェックのための統一された例外型

Kotlin 1.4.0以降、すべてのランタイムnullチェックは、KotlinNullPointerExceptionIllegalStateExceptionIllegalArgumentException、およびTypeCastExceptionの代わりにjava.lang.NullPointerExceptionをスローするようになります。これは、!!演算子、メソッドのプリアンブルにおけるパラメータのnullチェック、プラットフォーム型式でのnullチェック、および非null許容型に対するas演算子に適用されます。 これは、lateinit nullチェックやcheckNotNullrequireNotNullのような明示的なライブラリ関数呼び出しには適用されません。

この変更により、KotlinコンパイラまたはAndroidのR8オプティマイザなどのさまざまな種類のバイトコード処理ツールによって実行できるnullチェック最適化の数が増加します。

開発者の視点から見ると、大きな変更はありません。Kotlinコードは以前と同じエラーメッセージで例外をスローします。例外の型は変わりますが、渡される情報は同じままです。

JVMバイトコードにおける型アノテーション

Kotlinは、JVMバイトコード(ターゲットバージョン1.8以上)に型アノテーションを生成できるようになり、ランタイムでJavaリフレクションから利用できるようになりました。 バイトコードに型アノテーションを出力するには、以下の手順に従います。

  1. 宣言されたアノテーションが適切なアノテーションターゲット(JavaのElementType.TYPE_USEまたはKotlinのAnnotationTarget.TYPE)と保持ポリシー(AnnotationRetention.RUNTIME)を持っていることを確認します。
  2. アノテーションクラス宣言をJVMバイトコードターゲットバージョン1.8+にコンパイルします。これは-jvm-target=1.8コンパイラオプションで指定できます。
  3. アノテーションを使用するコードをJVMバイトコードターゲットバージョン1.8+(-jvm-target=1.8)にコンパイルし、-Xemit-jvm-type-annotationsコンパイラオプションを追加します。

標準ライブラリはターゲットバージョン1.6でコンパイルされているため、現時点では標準ライブラリからの型アノテーションはバイトコードに出力されないことに注意してください。

現在、基本的なケースのみがサポートされています。

  • メソッドパラメータ、メソッド戻り値型、およびプロパティ型に対する型アノテーション
  • Smth<@Ann Foo>Array<@Ann Foo>のような型引数の不変射影

以下の例では、String型に対する@Fooアノテーションはバイトコードに出力され、ライブラリコードで使用できます。

kotlin
@Target(AnnotationTarget.TYPE)
annotation class Foo

class A {
    fun foo(): @Foo String = "OK"
}

Kotlin/JS

JSプラットフォームでは、Kotlin 1.4.0は以下の改善を提供します。

新しいGradle DSL

kotlin.js Gradleプラグインには調整されたGradle DSLが付属しており、多数の新しい設定オプションを提供し、kotlin-multiplatformプラグインが使用するDSLにより密接に合わせられています。最も影響の大きい変更の一部は以下の通りです。

新しいJS IRバックエンド

現在Alpha安定性であるKotlin/JSのIRバックエンドは、デッドコード削除による生成コードサイズの削減、JavaScriptおよびTypeScriptとの相互運用性の改善など、Kotlin/JSターゲットに特化した新しい機能を提供します。

Kotlin/JS IRバックエンドを有効にするには、gradle.propertiesファイルでkotlin.js.compiler=irキーを設定するか、Gradleビルドスクリプトのjs関数にIRコンパイラタイプを渡します。

groovy
kotlin {
    js(IR) { // or: LEGACY, BOTH
        // ...
    }
    binaries.executable()
}

新しいバックエンドの設定方法に関する詳細情報については、Kotlin/JS IRコンパイラドキュメントを参照してください。

新しい@JsExportアノテーションと、**KotlinコードからTypeScript定義を生成する**機能により、Kotlin/JS IRコンパイラバックエンドはJavaScript&TypeScriptの相互運用性を向上させます。これにより、Kotlin/JSコードを既存のツールと統合し、ハイブリッドアプリケーションを作成し、マルチプラットフォームプロジェクトでコード共有機能を活用することも容易になります。

Kotlin/JS IRコンパイラバックエンドで利用可能な機能について詳しく学ぶ

Kotlin/Native

1.4.0では、Kotlin/Nativeに多くの新機能と改善が加えられました。これには以下が含まれます。

SwiftおよびObjective-CでのKotlinの中断関数(suspend function)のサポート

1.4.0では、SwiftおよびObjective-Cでの中断関数の基本的なサポートを追加しました。KotlinモジュールをAppleフレームワークにコンパイルすると、中断関数はコールバックを伴う関数(Swift/Objective-Cの用語ではcompletionHandler)として利用できるようになります。生成されたフレームワークのヘッダーにそのような関数がある場合、SwiftまたはObjective-Cコードからそれらを呼び出し、オーバーライドすることもできます。

例えば、次のKotlin関数を記述した場合:

kotlin
suspend fun queryData(id: Int): String = ...

...Swiftから次のように呼び出すことができます。

swift
queryData(id: 17) { result, error in
   if let e = error {
       print("ERROR: \(e)")
   } else {
       print(result!)
   }
}

SwiftおよびObjective-Cでの中断関数の使用について詳しく学ぶ

デフォルトでのObjective-Cジェネリクスのサポート

以前のバージョンのKotlinでは、Objective-Cの相互運用におけるジェネリクスに関する実験的なサポートが提供されていました。1.4.0以降、Kotlin/NativeはデフォルトでKotlinコードからジェネリクスを含むAppleフレームワークを生成します。場合によっては、これによりKotlinフレームワークを呼び出す既存のObjective-CまたはSwiftコードが壊れる可能性があります。ジェネリクスなしでフレームワークヘッダーを記述するには、-Xno-objc-genericsコンパイラオプションを追加します。

kotlin
kotlin {
    targets.withType<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget> {
        binaries.all {
            freeCompilerArgs += "-Xno-objc-generics"
        }
    }
}

Objective-Cとの相互運用に関するドキュメントに記載されているすべての詳細と制限事項は、引き続き有効です。

Objective-C/Swiftの相互運用における例外処理

1.4.0では、Kotlinから生成されるSwift APIについて、例外の変換方法に関してわずかに変更を加えています。KotlinとSwiftの間には、エラー処理において根本的な違いがあります。Kotlinの例外はすべて非チェック例外ですが、Swiftにはチェック済みエラーしかありません。したがって、Swiftコードが期待される例外を認識できるようにするには、Kotlin関数を@Throwsアノテーションでマークし、潜在的な例外クラスのリストを指定する必要があります。

SwiftまたはObjective-Cフレームワークにコンパイルする場合、@Throwsアノテーションを持つ、またはそれを継承する関数は、Objective-CではNSError*を生成するメソッドとして、Swiftではthrowsメソッドとして表現されます。

以前は、RuntimeExceptionError以外のすべての例外はNSErrorとして伝播されました。現在この動作は変更され、NSError@Throwsアノテーションのパラメータとして指定されたクラス(またはそのサブクラス)のインスタンスである例外に対してのみスローされます。Swift/Objective-Cに到達する他のKotlin例外は未処理と見なされ、プログラムを終了させます。

Appleターゲットでリリース.dSYMをデフォルトで生成

1.4.0以降、Kotlin/Nativeコンパイラは、Darwinプラットフォーム上のリリースバイナリに対してデフォルトでデバッグシンボルファイル.dSYM)を生成します。これは、-Xadd-light-debug=disableコンパイラオプションで無効にできます。他のプラットフォームでは、このオプションはデフォルトで無効になっています。Gradleでこのオプションを切り替えるには、次を使用します。

kotlin
kotlin {
    targets.withType<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget> {
        binaries.all {
            freeCompilerArgs += "-Xadd-light-debug={enable|disable}"
        }
    }
}

クラッシュレポートのシンボリケーションについて詳しく学ぶ

パフォーマンスの改善

Kotlin/Nativeは、開発プロセスと実行の両方を高速化する多くのパフォーマンス改善を受けました。以下にいくつかの例を示します。

  • オブジェクト割り当ての速度を改善するために、システムアロケータの代替としてmimallocメモリ割り当て器を提供するようになりました。mimallocは一部のベンチマークで最大2倍高速に動作します。 現在、Kotlin/Nativeにおけるmimallocの使用は実験的です。-Xallocator=mimallocコンパイラオプションを使用して切り替えることができます。

  • C相互運用ライブラリの構築方法を見直しました。新しいツールを使用すると、Kotlin/Nativeは以前よりも最大4倍高速に相互運用ライブラリを生成し、アーティファクトは以前の25%から30%のサイズになります。

  • GCの最適化により、全体的なランタイムパフォーマンスが向上しました。この改善は、多数の長期間存続するオブジェクトを持つプロジェクトで特に顕著になります。HashMapおよびHashSetコレクションは、冗長なボクシングを回避することで高速に動作するようになりました。

  • 1.3.70では、Kotlin/Nativeコンパイルのパフォーマンスを改善するための2つの新機能、プロジェクト依存関係のキャッシュとGradleデーモンからのコンパイラ実行を導入しました。 それ以来、私たちは多数の問題を修正し、これらの機能の全体的な安定性を向上させました。

CocoaPods依存関係の管理の簡素化

以前は、プロジェクトを依存関係マネージャーCocoaPodsと統合すると、マルチプラットフォームプロジェクトの他の部分とは別に、プロジェクトのiOS、macOS、watchOS、またはtvOS部分をXcodeでのみビルドできました。これらの他の部分はIntelliJ IDEAでビルドできました。

さらに、CocoaPodsに保存されているObjective-Cライブラリ(Podライブラリ)への依存関係を追加するたびに、IntelliJ IDEAからXcodeに切り替え、pod installを呼び出し、そこでXcodeビルドを実行する必要がありました。

これで、IntelliJ IDEAでPod依存関係を直接管理できるようになり、コードのハイライトや補完など、コードを扱う上でIntelliJ IDEAが提供するメリットを享受できます。Xcodeに切り替えることなく、GradleでKotlinプロジェクト全体をビルドすることもできます。これは、Swift/Objective-Cコードを記述したり、シミュレータまたはデバイスでアプリケーションを実行したりする必要がある場合にのみ、Xcodeに移動する必要があることを意味します。

また、ローカルに保存されているPodライブラリを操作することもできます。

必要に応じて、以下の間に依存関係を追加できます。

  • CocoaPodsリポジトリにリモートで保存されているPodライブラリ、またはローカルに保存されているPodライブラリとKotlinプロジェクト。
  • Kotlin Pod(CocoaPods依存関係として使用されるKotlinプロジェクト)と1つ以上のターゲットを持つXcodeプロジェクト。

初期設定を完了し、cocoapodsに新しい依存関係を追加する場合は、IntelliJ IDEAでプロジェクトを再インポートするだけです。新しい依存関係が自動的に追加されます。追加のステップは不要です。

依存関係の追加方法について詳しく学ぶ

Kotlin Multiplatform

DANGER

マルチプラットフォームプロジェクトのサポートはAlpha段階です。互換性のない変更があり、将来的に手動での移行が必要になる場合があります。

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

Kotlin Multiplatformは、異なるプラットフォーム向けに同じコードを記述・保守する時間を削減し、ネイティブプログラミングの柔軟性と利点を維持します。私たちは引き続きマルチプラットフォーム機能と改善に努力を投資しています。

NOTE

マルチプラットフォームプロジェクトにはGradle 6.0以降が必要です。

階層的なプロジェクト構造による複数のターゲットでのコード共有

新しい階層的なプロジェクト構造のサポートにより、マルチプラットフォームプロジェクト複数のプラットフォーム間でコードを共有できます。

以前は、マルチプラットフォームプロジェクトに追加されたコードは、プラットフォーム固有のソースセット(1つのターゲットに限定され、他のプラットフォームでは再利用できない)に配置されるか、commonMaincommonTestのような共通ソースセット(プロジェクト内のすべてのプラットフォームで共有される)に配置されるかのいずれかでした。共通ソースセットでは、プラットフォーム固有のactual実装を必要とするexpect宣言を使用することによってのみ、プラットフォーム固有のAPIを呼び出すことしかできませんでした。

これにより、すべてのプラットフォームでコードを共有するのは容易でしたが、特定のターゲット、特に似たようなターゲット間でのみ共有するのはそれほど容易ではありませんでした。似たようなターゲットは、多くの共通ロジックとサードパーティAPIを再利用できる可能性がありました。

例えば、iOSをターゲットとする典型的なマルチプラットフォームプロジェクトでは、iOS関連のターゲットが2つあります。1つはiOS ARM64デバイス用、もう1つはx64シミュレータ用です。これらは個別のプラットフォーム固有のソースセットを持っていますが、実際にはデバイスとシミュレータで異なるコードが必要となることは稀で、依存関係もよく似ています。そのため、iOS固有のコードをそれらの間で共有できます。

明らかに、この設定では、2つのiOSターゲットのための共有ソースセットを持ち、iOSデバイスとシミュレータの両方に共通のAPIをKotlin/Nativeコードから直接呼び出すことができるようにすることが望ましいでしょう。

Code shared for iOS targets

これで、階層的なプロジェクト構造のサポートにより、これが可能になりました。このサポートは、各ソースセットで利用可能なAPIと言語機能を、どのターゲットがそれらを消費するかに基づいて推論し、適応させます。

ターゲットの一般的な組み合わせについては、ターゲットショートカットを使用して階層構造を作成できます。 例えば、ios()ショートカットを使用して、上記の2つのiOSターゲットと共有ソースセットを作成します。

kotlin
kotlin {
    ios() // iOS device and simulator targets; iosMain and iosTest source sets
}

他のターゲットの組み合わせについては、dependsOn関係でソースセットを接続して手動で階層を作成します

Hierarchical structure

kotlin
kotlin{
    sourceSets {
        val desktopMain by creating {
            dependsOn(commonMain)
        }
        val linuxX64Main by getting {
            dependsOn(desktopMain)
        }
        val mingwX64Main by getting {
            dependsOn(desktopMain)
        }
        val macosX64Main by getting {
            dependsOn(desktopMain)
        }
    }
}
groovy
kotlin {
    sourceSets {
        desktopMain {
            dependsOn(commonMain)
        }
        linuxX64Main {
            dependsOn(desktopMain)
        }
        mingwX64Main {
            dependsOn(desktopMain)
        }
        macosX64Main {
            dependsOn(desktopMain)
        }
    }
}

階層的なプロジェクト構造のおかげで、ライブラリもターゲットのサブセットに対して共通APIを提供できます。 ライブラリでのコード共有について詳しく学ぶ

階層構造におけるネイティブライブラリの活用

Foundation、UIKit、POSIXなどのプラットフォーム依存ライブラリを、複数のネイティブターゲット間で共有されるソースセットで使用できます。これにより、プラットフォーム固有の依存関係に制限されることなく、より多くのネイティブコードを共有するのに役立ちます。

追加のステップは不要です。すべて自動的に行われます。IntelliJ IDEAは、共有コードで使用できる共通の宣言を検出するのに役立ちます。

プラットフォーム依存ライブラリの使用について詳しく学ぶ

依存関係を一度だけ指定する

これからは、共有ソースセットとプラットフォーム固有のソースセットで使用される同じライブラリの異なるバリアントに対する依存関係をそれぞれ指定する代わりに、共有ソースセットで一度だけ依存関係を指定する必要があります。

kotlin
kotlin {
    sourceSets {
        val commonMain by getting {
            dependencies {
                implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2")
            }
        }
    }
}
groovy
kotlin {
    sourceSets {
        commonMain {
            dependencies {
                implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2'
            }
        }
    }
}

-common-nativeなどのプラットフォームを指定するサフィックスを持つkotlinxライブラリアーティファクト名は、もはやサポートされていないため、使用しないでください。代わりに、上記の例ではkotlinx-coroutines-coreであるライブラリのベースアーティファクト名を使用してください。

ただし、この変更は現在のところ、以下には影響しません。

  • stdlibライブラリ – Kotlin 1.4.0以降、stdlib依存関係は自動的に追加されます
  • kotlin.testライブラリ – 引き続きtest-commontest-annotations-commonを使用する必要があります。これらの依存関係は後で対処されます。

特定のプラットフォームのみに依存関係が必要な場合は、-jvm-jsのようなサフィックスを持つ標準およびkotlinxライブラリのプラットフォーム固有のバリアントを、例えばkotlinx-coroutines-core-jvmのように引き続き使用できます。

依存関係の設定について詳しく学ぶ

Gradleプロジェクトの改善

Kotlin MultiplatformKotlin/JVMKotlin/NativeKotlin/JSに固有のGradleプロジェクト機能と改善に加えて、すべてのKotlin Gradleプロジェクトに適用されるいくつかの変更があります。

標準ライブラリへの依存関係がデフォルトで追加されるようになりました

マルチプラットフォームプロジェクトを含む、すべてのKotlin Gradleプロジェクトでstdlibライブラリへの依存関係を宣言する必要がなくなりました。 依存関係はデフォルトで追加されます。

自動的に追加される標準ライブラリは、Kotlin Gradleプラグインと同じバージョンになります。これらは同じバージョン管理を使用しているためです。

プラットフォーム固有のソースセットには、対応するプラットフォーム固有のライブラリバリアントが使用され、残りの部分には共通の標準ライブラリが追加されます。Kotlin Gradleプラグインは、GradleビルドスクリプトのkotlinOptions.jvmTargetコンパイラオプションに応じて、適切なJVM標準ライブラリを選択します。

デフォルトの振る舞いを変更する方法について学ぶ

Kotlinプロジェクトには最新バージョンのGradleが必要

Kotlinプロジェクトで新機能を楽しむには、Gradleを最新バージョンに更新してください。 マルチプラットフォームプロジェクトにはGradle 6.0以降が必要ですが、他のKotlinプロジェクトはGradle 5.4以降で動作します。

IDEにおける*.gradle.ktsサポートの改善

1.4.0では、Gradle Kotlin DSLスクリプト(*.gradle.ktsファイル)に対するIDEサポートの改善を続けました。新しいバージョンがもたらすものは以下の通りです。

  • パフォーマンス向上のための_スクリプト構成の明示的な読み込み_。以前は、ビルドスクリプトへの変更がバックグラウンドで自動的に読み込まれていました。パフォーマンスを改善するため、1.4.0ではビルドスクリプト構成の自動読み込みを無効にしました。IDEは、明示的に適用した場合にのみ変更を読み込みます。

    Gradle 6.0より前のバージョンでは、エディタでLoad Configurationをクリックしてスクリプト構成を手動で読み込む必要があります。

    *.gradle.kts – Load Configuration

    Gradle 6.0以降では、Load Gradle Changesをクリックするか、Gradleプロジェクトを再インポートすることで、明示的に変更を適用できます。

    IntelliJ IDEA 2020.1とGradle 6.0以降では、Load Script Configurationsというアクションが追加されました。これは、プロジェクト全体を更新せずにスクリプト構成への変更を読み込みます。これは、プロジェクト全体を再インポートするよりもはるかに短い時間で済みます。

    *.gradle.kts – Load Script Changes and Load Gradle Changes

    新しく作成されたスクリプトや、新しいKotlinプラグインを使用して初めてプロジェクトを開く場合も、Load Script Configurationsを行う必要があります。

    Gradle 6.0以降では、以前の個別に読み込まれる実装とは異なり、すべてのスクリプトを一度に読み込むことができるようになりました。各リクエストにはGradle構成フェーズの実行が必要となるため、大規模なGradleプロジェクトではリソースを大量に消費する可能性がありました。

    現在、このような読み込みはbuild.gradle.ktsおよびsettings.gradle.ktsファイルに限定されています(関連する課題に投票してください)。 init.gradle.ktsまたは適用されたスクリプトプラグインのハイライトを有効にするには、古いメカニズム(スタンドアロンスクリプトに追加する)を使用してください。これらのスクリプトの構成は、必要に応じて個別に読み込まれます。 そのようなスクリプトの自動リロードを有効にすることもできます。

    *.gradle.kts – Add to standalone scripts

  • より良いエラー報告。以前は、Gradleデーモンからのエラーを別のログファイルでしか確認できませんでした。現在、Gradleデーモンはエラーに関するすべての情報を直接返し、ビルドツールウィンドウに表示します。これにより、時間と労力の両方を節約できます。

標準ライブラリ

以下は、1.4.0のKotlin標準ライブラリにおける最も重要な変更点です。

共通の例外処理API

以下のAPI要素は共通ライブラリに移動されました。

  • このThrowableとそのスタックトレースの詳細な記述を返すThrowable.stackTraceToString()拡張関数、およびこの記述を標準エラー出力にプリントするThrowable.printStackTrace()
  • 例外をデリバーするために抑制された例外を指定できるThrowable.addSuppressed()関数、およびすべての抑制された例外のリストを返すThrowable.suppressedExceptionsプロパティ。
  • 関数がプラットフォームメソッド(JVMまたはネイティブプラットフォーム上)にコンパイルされる際にチェックされる例外型をリストする@Throwsアノテーション。

配列とコレクションのための新しい関数

コレクション

1.4.0では、標準ライブラリにコレクションを扱うための便利な関数が多数含まれています。

  • setOfNotNull()は、提供された引数の中からすべての非null項目で構成されるセットを作成します。

    kotlin
    fun main() {
        val set = setOfNotNull(null, 1, 2, 0, null)
        println(set)
    }

  • シーケンスのためのshuffled()

    kotlin
    fun main() {
        val numbers = (0 until 50).asSequence()
        val result = numbers.map { it * 2 }.shuffled().take(5)
        println(result.toList()) //five random even numbers below 100
    }

  • onEach()flatMap()*Indexed()対応関数。 これらがコレクション要素に適用する操作は、要素インデックスをパラメータとして持ちます。

    kotlin
    fun main() {
        listOf("a", "b", "c", "d").onEachIndexed {
            index, item -> println(index.toString() + ":" + item)
        }
    
       val list = listOf("hello", "kot", "lin", "world")
              val kotlin = list.flatMapIndexed { index, item ->
                  if (index in 1..2) item.toList() else emptyList() 
              }
              println(kotlin)
    }

  • randomOrNull()reduceOrNull()reduceIndexedOrNull()*OrNull()対応関数。 これらは空のコレクションに対してnullを返します。

    kotlin
    fun main() {
         val empty = emptyList<Int>()
         empty.reduceOrNull { a, b -> a + b }
         //empty.reduce { a, b -> a + b } // Exception: Empty collection can't be reduced.
    }

  • runningFold()、その同義語であるscan()、およびrunningReduce()は、fold()およびreduce()と同様に、指定された操作をコレクション要素に順次適用します。違いは、これらの新しい関数が中間結果のシーケンス全体を返すことです。

    kotlin
    fun main() {
        val numbers = mutableListOf(0, 1, 2, 3, 4, 5)
        val runningReduceSum = numbers.runningReduce { sum, item -> sum + item }
        val runningFoldSum = numbers.runningFold(10) { sum, item -> sum + item }
        println(runningReduceSum.toString())
        println(runningFoldSum.toString())
    }

  • sumOf()はセレクタ関数を受け取り、コレクションのすべての要素に対するその値の合計を返します。 sumOf()IntLongDoubleUIntULong型の合計を生成できます。JVMでは、BigIntegerBigDecimalも利用可能です。

    kotlin
    data class OrderItem(val name: String, val price: Double, val count: Int)
    
    fun main() {
        val order = listOf<OrderItem>(
            OrderItem("Cake", price = 10.0, count = 1),
            OrderItem("Coffee", price = 2.5, count = 3),
            OrderItem("Tea", price = 1.5, count = 2))
    
        val total = order.sumOf { it.price * it.count } // Double
        val count = order.sumOf { it.count } // Int
        println("You've ordered $count items that cost $total in total")
    }

  • min()およびmax()関数は、KotlinコレクションAPI全体で使用されている命名規則に準拠するためにminOrNull()およびmaxOrNull()に改名されました。関数名の*OrNullサフィックスは、レシーバコレクションが空の場合にnullを返すことを意味します。minBy()maxBy()minWith()maxWith()にも同様に適用され、1.4では*OrNull()対応関数が追加されています。

  • 新しいminOf()およびmaxOf()拡張関数は、コレクションの項目に対する指定されたセレクタ関数の最小値と最大値を返します。

    kotlin
    data class OrderItem(val name: String, val price = 10.0, val count = 1), // added "val count = 1" to remove unused variable warning
            OrderItem("Coffee", price = 2.5, count = 3),
            OrderItem("Tea", price = 1.5, count = 2))
        val highestPrice = order.maxOf { it.price }
        println("The most expensive item in the order costs $highestPrice")
    }

    Comparatorを引数に取るminOfWith()およびmaxOfWith()、および空のコレクションに対してnullを返すこれら4つの関数の*OrNull()バージョンもあります。

  • flatMapおよびflatMapToの新しいオーバーロードにより、レシーバ型と一致しない戻り値型を持つ変換を使用できます。具体的には:

    • IterableArrayMapに対するSequenceへの変換
    • Sequenceに対するIterableへの変換
    kotlin
    fun main() {
        val list = listOf("kot", "lin")
        val lettersList = list.flatMap { it.asSequence() }
        val lettersSeq = list.asSequence().flatMap { it.toList() }    
        println(lettersList)
        println(lettersSeq.toList())
    }

  • ミュータブルリストから要素を削除するためのremoveFirst()およびremoveLast()ショートカット、およびこれらの関数の*orNull()対応関数。

配列

異なるコンテナ型を扱う際に一貫したエクスペリエンスを提供するために、配列のための新しい関数も追加しました。

  • shuffle()は配列要素をランダムな順序に並べます。
  • onEach()は各配列要素に対して与えられたアクションを実行し、配列自体を返します。
  • associateWith()associateWithTo()は配列要素をキーとしてマップを構築します。
  • 配列サブレンジのreverse()はサブレンジ内の要素の順序を反転させます。
  • 配列サブレンジのsortDescending()はサブレンジ内の要素を降順にソートします。
  • 配列サブレンジのsort()sortWith()は、共通ライブラリで利用可能になりました。
kotlin
fun main() {
    var language = ""
    val letters = arrayOf("k", "o", "t", "l", "i", "n")
    val fileExt = letters.onEach { language += it }
       .filterNot { it in "aeuio" }.take(2)
       .joinToString(prefix = ".", separator = "")
    println(language) // "kotlin"
    println(fileExt) // ".kt"

    letters.shuffle()
    letters.reverse(0, 3)
    letters.sortDescending(2, 5)
    println(letters.contentToString()) // [k, o, t, l, i, n]
}

さらに、CharArray/ByteArrayString間の変換のための新しい関数があります。

  • ByteArray.decodeToString()およびString.encodeToByteArray()
  • CharArray.concatToString()およびString.toCharArray()
kotlin
fun main() {
	val str = "kotlin"
    val array = str.toCharArray()
    println(array.concatToString())
}

ArrayDeque

両端キューの実装であるArrayDequeクラスも追加しました。 両端キューは、キューの先頭または末尾の両方で償却定数時間で要素を追加または削除できます。コードでキューまたはスタックが必要な場合は、デフォルトで両端キューを使用できます。

kotlin
fun main() {
    val deque = ArrayDeque(listOf(1, 2, 3))

    deque.addFirst(0)
    deque.addLast(4)
    println(deque) // [0, 1, 2, 3, 4]

    println(deque.first()) // 0
    println(deque.last()) // 4

    deque.removeFirst()
    deque.removeLast()
    println(deque) // [1, 2, 3]
}

ArrayDequeの実装は、内部では可変長配列を使用しています。コンテンツを循環バッファであるArrayに格納し、このArrayがいっぱいになった場合にのみサイズを変更します。

文字列操作のための関数

1.4.0の標準ライブラリには、文字列操作のためのAPIに多くの改善が含まれています。

  • StringBuilderには便利な新しい拡張関数があります。set()setRange()deleteAt()deleteRange()appendRange()などです。

    kotlin
        fun main() {
            val sb = StringBuilder("Bye Kotlin 1.3.72")
            sb.deleteRange(0, 3)
            sb.insertRange(0, "Hello", 0 ,5)
            sb.set(15, '4')
            sb.setRange(17, 19, "0")
            print(sb.toString())
        }

  • StringBuilderの既存の関数の一部は、共通ライブラリで利用可能です。これには、append()insert()substring()setLength()などが含まれます。

  • 新しい関数Appendable.appendLine()およびStringBuilder.appendLine()が共通ライブラリに追加されました。これらは、JVMのみのappendln()関数を置き換えます。

    kotlin
    fun main() {
        println(buildString {
            appendLine("Hello,")
            appendLine("world")
        })
    }

ビット操作

ビット操作のための新しい関数です。

  • countOneBits()
  • countLeadingZeroBits()
  • countTrailingZeroBits()
  • takeHighestOneBit()
  • takeLowestOneBit()
  • rotateLeft()rotateRight()(実験的)
kotlin
fun main() {
    val number = "1010000".toInt(radix = 2)
    println(number.countOneBits())
    println(number.countTrailingZeroBits())
    println(number.takeHighestOneBit().toString(2))
}

委譲プロパティの改善

1.4.0では、Kotlinでの委譲プロパティの体験を改善するための新機能が追加されました。

  • プロパティは別のプロパティに委譲できるようになりました。
  • 新しいインターフェースPropertyDelegateProviderは、単一の宣言でデリゲートプロバイダを作成するのに役立ちます。
  • ReadWritePropertyReadOnlyPropertyを継承するようになったため、読み取り専用プロパティに両方を使用できます。

新しいAPIとは別に、結果として生成されるバイトコードサイズを削減するいくつかの最適化を行いました。これらの最適化については、このブログ記事で説明されています。

委譲プロパティについて詳しく学ぶ

KTypeからJava Typeへの変換

stdlibの新しい拡張プロパティKType.javaType(現在は実験的)は、kotlin-reflect全体の依存関係を使用せずに、Kotlin型からjava.lang.reflect.Typeを取得するのに役立ちます。

kotlin
import kotlin.reflect.javaType
import kotlin.reflect.typeOf

@OptIn(ExperimentalStdlibApi::class)
inline fun <reified T> accessReifiedTypeArg() {
   val kType = typeOf<T>()
   println("Kotlin type: $kType")
   println("Java type: ${kType.javaType}")
}

@OptIn(ExperimentalStdlibApi::class)
fun main() {
   accessReifiedTypeArg<String>()
   // Kotlin type: kotlin.String
   // Java type: class java.lang.String
  
   accessReifiedTypeArg<List<String>>()
   // Kotlin type: kotlin.collections.List<kotlin.String>
   // Java type: java.util.List<java.lang.String>
}

KotlinリフレクションのためのProguard設定

1.4.0以降、KotlinリフレクションのためのProguard/R8設定がkotlin-reflect.jarに組み込まれました。これにより、R8またはProguardを使用するほとんどのAndroidプロジェクトは、追加の設定なしでkotlin-reflectと連携するはずです。 kotlin-reflectの内部のためのProguardルールをコピー&ペーストする必要はなくなりました。ただし、リフレクトする予定のすべてのAPIを明示的にリストする必要はまだありますので注意してください。

既存APIの改善

  • いくつかの関数はnullレシーバーでも機能するようになりました。例えば:

    • 文字列に対するtoBoolean()
    • 配列に対するcontentEquals()contentHashcode()contentToString()
  • DoubleおよびFloatにおけるNaNNEGATIVE_INFINITYPOSITIVE_INFINITYconstとして定義されるようになったため、アノテーション引数として使用できます。

  • DoubleおよびFloatに新しい定数SIZE_BITSSIZE_BYTESが追加され、型のインスタンスをバイナリ形式で表現するために使用されるビット数とバイト数が含まれています。

  • maxOf()およびminOf()トップレベル関数は、可変数の引数(vararg)を受け入れることができます。

stdlibアーティファクトのmodule-infoディスクリプタ

Kotlin 1.4.0は、デフォルトの標準ライブラリアーティファクトにmodule-info.javaモジュール情報を追加します。これにより、アプリケーションに必要なプラットフォームモジュールのみを含むカスタムJavaランタイムイメージを生成するjlinkツールでそれらを使用できるようになります。 以前からKotlin標準ライブラリアーティファクトでjlinkを使用できましたが、そのためには別のアーティファクト("modular"分類子を持つもの)を使用する必要があり、全体的な設定は簡単ではありませんでした。 Androidでは、module-infoを含むjarファイルを正しく処理できるAndroid Gradleプラグインバージョン3.2以降を使用していることを確認してください。

非推奨機能

DoubleとFloatのtoShort()とtoByte()

DoubleFloattoShort()およびtoByte()関数は、値の範囲が狭く、変数のサイズが小さいため、予期せぬ結果を招く可能性があったため、非推奨になりました。

浮動小数点数をByteまたはShortに変換するには、まずIntに変換し、次にターゲット型に変換するという2段階変換を使用してください。

浮動小数点配列のcontains()、indexOf()、lastIndexOf()

FloatArrayおよびDoubleArraycontains()indexOf()、およびlastIndexOf()拡張関数は、IEEE 754標準の等価性を使用しており、これが一部のコーナーケースで全順序の等価性と矛盾するため、非推奨になりました。この課題を参照してください。

min()およびmax()コレクション関数

min()およびmax()コレクション関数は、空のコレクションに対してnullを返すという振る舞いをより適切に反映するminOrNull()maxOrNull()が優先され、非推奨になりました。 この課題を参照してください。

非推奨の実験的コルーチンの除外

kotlin.coroutines.experimental APIは1.3.0でkotlin.coroutinesに置き換えられ、非推奨になりました。1.4.0では、kotlin.coroutines.experimentalの非推奨サイクルを完了し、標準ライブラリから削除します。JVM上でまだ使用している方のために、すべての実験的コルーチンAPIを含む互換性アーティファクトkotlin-coroutines-experimental-compat.jarを提供しています。これはMavenに公開されており、標準ライブラリとともにKotlinディストリビューションに含めています。

安定版JSONシリアライゼーション

Kotlin 1.4.0では、kotlinx.serializationの最初の安定版である1.0.0-RCを出荷します。これにより、kotlinx-serialization-core(以前はkotlinx-serialization-runtimeとして知られていた)のJSONシリアライゼーションAPIが安定版であることを発表できることを嬉しく思います。他のシリアライゼーション形式のライブラリは引き続き実験的であり、コアライブラリの一部の高度な部分も同様です。

JSONシリアライゼーションのAPIは、より一貫性があり使いやすいものにするために大幅に見直しました。今後は、後方互換性のある方法でJSONシリアライゼーションAPIの開発を続けます。 ただし、以前のバージョンを使用していた場合は、1.0.0-RCに移行する際にコードの一部を書き直す必要があります。 これをお手伝いするために、kotlinx.serializationの完全なドキュメントセットである**Kotlin Serialization Guide**も提供しています。これは、最も重要な機能の使用プロセスを案内し、直面する可能性のある問題を解決するのに役立ちます。

NOTE

: kotlinx-serialization 1.0.0-RCはKotlinコンパイラ1.4でのみ動作します。以前のコンパイラバージョンとは互換性がありません。

スクリプティングとREPL

1.4.0では、Kotlinでのスクリプティングは、機能およびパフォーマンスの改善、その他の更新の恩恵を受けています。 主な変更点をいくつかご紹介します。

Kotlinでのスクリプティングに慣れるために、例を含むプロジェクトを用意しました。 これには、標準スクリプト(*.main.kts)の例と、KotlinスクリプティングAPIおよびカスタムスクリプト定義の使用例が含まれています。課題追跡システムを使用して、ぜひお試しいただき、フィードバックをお寄せください。

新しい依存関係解決API

1.4.0では、外部依存関係(Mavenアーティファクトなど)を解決するための新しいAPIと、その実装を導入しました。このAPIは、新しいアーティファクトkotlin-scripting-dependenciesおよびkotlin-scripting-dependencies-mavenで公開されています。 kotlin-script-utilライブラリの以前の依存関係解決機能は非推奨になりました。

新しいREPL API

新しい実験的なREPL APIがKotlinスクリプティングAPIの一部になりました。公開されたアーティファクトにはいくつかの実装があり、コード補完などの高度な機能を持つものもあります。私たちはKotlin JupyterカーネルでこのAPIを使用しており、独自のカスタムシェルやREPLで試すことができます。

コンパイル済みスクリプトキャッシュ

KotlinスクリプティングAPIは、コンパイル済みスクリプトキャッシュを実装する機能を提供するようになりました。これにより、変更されていないスクリプトのその後の実行が大幅に高速化します。デフォルトの高度なスクリプト実装であるkotlin-main-ktsは、すでに独自のキャッシュを持っています。

アーティファクト名の変更

アーティファクト名に関する混乱を避けるため、kotlin-scripting-jsr223-embeddablekotlin-scripting-jvm-host-embeddableを、それぞれkotlin-scripting-jsr223kotlin-scripting-jvm-hostに改名しました。これらのアーティファクトは、バンドルされたサードパーティライブラリをシェードして使用競合を回避するkotlin-compiler-embeddableアーティファクトに依存します。この名前変更により、kotlin-compiler-embeddable(一般的に安全です)の使用をスクリプティングアーティファクトのデフォルトにしています。 何らかの理由で、シェードされていないkotlin-compilerに依存するアーティファクトが必要な場合は、-unshadedサフィックスを持つアーティファクトバージョン(例:kotlin-scripting-jsr223-unshaded)を使用してください。この名前変更は、直接使用されることになっているスクリプティングアーティファクトにのみ影響し、他のアーティファクト名は変更されません。

Kotlin 1.4.0への移行

Kotlinプラグインの移行ツールは、プロジェクトを以前のバージョンのKotlinから1.4.0に移行するのに役立ちます。

Kotlinバージョンを1.4.0に変更し、GradleまたはMavenプロジェクトを再インポートするだけです。IDEはその後、移行について尋ねます。

同意すると、コードをチェックし、動作しないものや1.4.0で推奨されないものについて修正を提案する移行コードインスペクションが実行されます。

Run migration

コードインスペクションにはさまざまな重要度レベルがあり、どの提案を受け入れ、どれを無視するかを決定するのに役立ちます。

Migration inspections

Kotlin 1.4.0は機能リリースであり、言語に互換性のない変更をもたらす可能性があります。そのような変更の詳細なリストは、**Kotlin 1.4互換性ガイド**に記載されています。