アプリ開発備忘録

PlayStationMobile、Android、UWPの開発備忘録

Kotlin1.4の変更点

以下をざっと紹介しいきます。
https://kotlinlang.org/docs/reference/whatsnew14.html

SAM変換

Androidで言うと setOnClickListener がよく使っていると思います。

public interface OnClickListener {
    void onClick(View var1);
}

JavaのInterfaceで関数を一つだけ持っている場合、以下の構文が使用できていました。

view.setOnClickListener { view ->

}

1.4からは、Kotlinでも fun interface を使用することでこれを実現できるようになりました。

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

明示的なアクセス修飾子の設定

アクセス修飾子がない場合、 public を指定されたものとなりますが、以下の設定により、アクセス修飾子が設定されていない場合、エラーや警告を出すことができるようになりました。これはライブラリの作成などに役立ちます。

更に、publicに指定された場合、明示的に型を指定しないといけなくなります。

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

名前付き引数の混在

1.3までは、以下のように名前付き引数の後ろに名前無しの引数を入れることができませんでした。1.4からは、引数の順番が合っている場合に限ってこの制約が無くなりました。

fun hoge(
    one: String,
    two: String,
    three: String
) {
    hoge(
         "one",
        two ="two",
        "three"
    )
}

末尾のコンマ

末尾のコンマが許可されました。これにより要素の追加をしてもgitの差分が出ない、要素の入れ替えが簡単になるなどの利点があります。

fun hoge(
    one: String,
    two: String,
    three: String,
) {
    listOf(
        one,
        two,
        three,
    )
}

リフレクション(呼び出し可能な参照)の改善

以下のような構文がありました。これの改善になります。

"value".apply(::println)
// equal
println("println")

デフォルト引数

デフォルト引数が使用できるようになりました。

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

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

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

Unit

foo は引数に () -> Unit を取りますが、戻り値がUnitの場合、何でも指定できるようになりました。以下では () -> Int が渡されています。

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

fun main() {
    foo(::returnsInt)
}

可変長引数

可変長引数に適合するようになりました。全てコンパイルが通ります。

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 functionの対応

1.4以前では使用できませんでしたが、suspend functionにも使用できるようになりました。

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

fun test() {
    takeSuspend(::call)
}

Loopのラベル

Kotlin1.4では以下の文法が使用できます。

fun hoge() {
    for (item in 0 until 10) {
        when(item) {
            0 -> continue
            5 -> break
            else -> println(item)
        }
    }
}

hoge() // 1, 2, 3, 4

それ以前ではラベルを付与しなければいけませんでした。

fun hoge() {
    LOOP@for (item in 0 until 10) {
        when(item) {
            0 -> continue@LOOP
            5 -> break@LOOP
            else -> println(item)
        }
    }
}

fun main() {
    hoge() // 1, 2, 3, 4
}

型推論の改善

新しい型推論アルゴリズムが導入されました。例として、以下の記述は解釈に失敗していましたが、これが改善されました。

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) }
)

f:id:matsudamper:20200824070308p:plain

ラムダの末尾

ラムダの末尾がスマートキャストされるようになりました。1.3では String? 、1.4では String として解釈されます。

val result = run {
    var str = currentValue()
    if (str == null) {
        str = "test"
    }
    str // the Kotlin compiler knows that str is not null here
}

KCallableのスマートキャスト

kFunctionKCallable にスマートキャストされているので、 call が呼べます。

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

Delegation

1.3では解釈できませんでしたが、これが解釈できるようになりました。

import kotlin.properties.Delegates

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

1.3ではラムダの方に型を入れて、初めて解釈できるようになっていました。

import kotlin.properties.Delegates

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

SAM変換

複数引数のうち、末尾だけSAM変換をしたい場合にできるようになりました。

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

SAM interfaceを持っているものを関数の引数に取って、SAM変換できるようになりました。

fun interface IntPredicate {
    fun accept(i: Int): Boolean
}
fun foo(r: IntPredicate) {}
fun test() {
    foo {
        false
    }
}

標準ライブラリ

まとめると

  • 便利な関数を追加
  • OrNull系を追加
  • Array, Iterable, Sequenceを同じように使えるように近づけた
    • この3つは別のものだから別々に実装されている

標準API

  • setOfNotNull
    • listOfNotNull の Set版
  • Sequenceにshuffledが追加
  • onEachとflatMapにIndexedが追加
  • random, reduce, reduceIndexedにOrNullが追加
  • Iterable<T>.running* Fold, Reduceが追加
    • 途中の計算結果を保持したListを返す
  • sumOfの追加
    • 型を変えられるようになった
    • Int, Long, Double, UInt,,ULong, BigInteger, BigDecimalに対応
listOf(10, 20, 30).sumBy { it * 0.1 } // NG
listOf(10, 20, 30).sumOf { it * 0.1 } // OK
  • minOrNull, maxOrNullの追加
  • minBy, maxBy, minWith, maxWithにOrNullが追加
    • これに伴いmaxBy等、元のものが非推奨に
  • minOf, maxOfの追加
    • これに伴いmaxBy, maxByが非推奨に
  • minOfWith, maxOfWithにOrNullが追加
  • IterableとSequenceのflatMapがお互いをサポート
  • removeFirst, removeLastにOrNullが追加
    • NoSuchElementExceptionがスローされない
    • 削除できない場合、戻り値がNullに

Array

  • 以下を追加
    • shuffle
    • onEach
    • associateWith, associateWithTo
    • reverse
    • sortDescending
    • sort, sortWith
  • CharArray, ByteArrayのStringとの変換サポートの追加
    • ByteArray.decodeToString(), String.encodeToByteArray()
    • CharArray.concatToString(), String.toCharArray()
  • ArrayDequeの追加

文字列操作

  • StringBuilderに色々追加
  • その他共通ライブラリに追加

ビット操作

  • 色々追加

Delegate

  • fun interface PropertyDelegateProvider の追加
    • operator fun provideDelegate を使わなくても良くなったとかそんな感じなのかな。
  • ReadWritePropertyはReadOnlyPropertyを継承するように
    • ReadWritePropertyをReadOnlyに使用できるように
  • バイトコードの削減

リフレクション

  • KType.javaTypeの追加
    • kotlin.reflect 全体が必要無くなる
    • @OptIn(ExperimentalStdlibApi::class)が必要
  • Proguard/R8への組み込み
    • 追加の記述無しでkotlin-reflectが使用できる

その他

  • NullableでtoBoolean等が使えるようになった
  • トップレベルのmaxOf, minOfが可変長引数対応
  • DoubleのtoShort()の非推奨
    • toIntをしてからtoShortにする。(一気に失われる情報が多いので)
  • 浮動小数点でcontains, indexOf, lastIndexOfの廃止
  • kotlin.coroutines.experimentalの廃止
  • kotlinx-serializationが安定版に
    • またExperimentalなのに変わりはない

その他

  • IntelliJの新しいプロジェクトウィザード
    • MPPとかが簡単に作れるように
  • Coroutineのデバッガ
  • Kotlin/JVM , Kotlin/JS のバックエンドを統合中
  • Java1.8のinterfaceのdefaultキーワードにとして扱うためのオプションが追加
  • java.lang.NullPointerException の代わりに KotlinNullPointerException がスローされる
    • Android R8周りで最適化される。
  • Java8以降でバイトコードに型アノテーションを出力できるようになり、実行時にリフレクションできるようになった。
@Target(AnnotationTarget.TYPE)
annotation class Foo

class A {
    fun foo(): @Foo String = "OK"
}
  • 各プラットフォームの改善
  • Gradleサポートの改善
  • Kotlin Scriptの改善