Kotlin 1.4 プレビュー版がリリースされたので主に型推論の部分を眺めてみた 

# はじめに

さて、アドベントカレンダー以来の記事となります。
Qiitaはみてばかりですが、たまには投稿もしないとな、と思っていたところで、
Kotlin 1.4のプレビュー版M1がリリースされたので、今日はその辺りの記事を書いてみました。
と言っても、基本的には下記の公式記事をなぞってみた感じになります。

- https://blog.jetbrains.com/kotlin/2020/03/kotlin-1-4-m1-released/
- https://github.com/JetBrains/kotlin/releases/tag/v1.4-M1

今回のKotlinの更新は、概ね以前の記事の内容に沿っている認識です。（マルチプラットフォーム周りはどうなるか・・・）

- https://blog.jetbrains.com/kotlin/2019/12/what-to-expect-in-kotlin-1-4-and-beyond/#language-features

以前の記事で書かれていなかった内容としては、型推論やContractsの話あたりでしょうか。
色々あるのでこの記事では基本的には公式記事に沿って、主に型推論のところを中心に取り上げてみました。
特に、1.3との違いを意識して書いています。

なお、文中のソースコードは[公式記事](https://blog.jetbrains.com/kotlin/2020/03/kotlin-1-4-m1-released/)から引用していますが、説明のため＆警告が気になったので一部変更しているところがあります。
また、動作確認(挙動の比較も含む)は、[playground](https://play.kotlinlang.org/)で行いました。

# 型推論の改良
- Kotlinのクラス、インタフェースのSAM(Single Abstract Method)変換

これまではJavaのクラス、インタフェースについて、明示的なインスタンスの生成を行わなくてもlambdaを渡せば自動的に実装ができました。（AndroidアプリのsetOnClickListenerとかがわかりやすい例ですね）
1.4ではKotlinのクラス、インタフェースについても同様の実装ができる様になります。

以下が公式のサンプルです。
下記の通りlambdaを渡せばよくなります。
interfaceにfunが付与されているところもポイントになります。

```kotlin
fun interface Action {
    fun run()
}

fun runAction(a: Action) = a.run()

fun main() {
    runAction {
        println("Hello, Kotlin 1.4!")
    }
}
```

1.3では以下の様な感じでしょうか。
Action#runを明示的に実装する必要があります。

```kotlin
interface Action {
    fun run()
}

class ActionImpl:Action {
    override fun run() {println("Hello, Kotlin 1.3!")}
}

fun runAction(a: Action) = a.run()

fun main() {
    runAction(ActionImpl())
}
```

- これまで型の明確化が必要だったケースについて対応。

例えば、lambdaのitの型推論が、正しく行われるようになった。（というかそもそもこんな形でitが使える様になった。1.3.70ではエラーになる）

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

これまでだと、こんな感じでPairを使うことになり、かつlambdaのパラメータを明記する必要がありました。

```kotlin
val rulesMap: Map<String, (String?) -> Boolean> = mapOf( 
   Pair("weak", {i -> i != null}) 
)
```

- lambdaの最後の行（戻り値になる）に対するSmart cast

1.3では以下の場合は`String?`型で返却。1.4では`String`型で返却。

```kotlin
val result = run { 
    var str = currentValue() 
    if (str == null) { str = "test" }
    str 
}
```

- Smart Castにより変換されたクラスのメンバにアクセスすることができる

以下のanimal::meow、animal::woofはキャスト後のクラスのメンバ。
1.3ではエラーになる。

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

- デフォルト値を設定した関数の推論が便利になった

1.3では、引数にデフォルト値を設定した場合、その型をパラメータとした関数として扱われた。
1.4では引数なしの関数としても扱うことができる。

例えば、以下の例で、apply1について、1.3ではエラーとなっていた。  
（foo関数は(Int) -> Stringの関数として扱われるため。）

```kotlin
fun foo(i: Int = 0): String = "$i!" 
fun apply1(func: () -> String): String = func() 
fun apply2(func: (Int) -> String): String = func(42) 
fun main() {
     println(apply1(::foo)) 
     println(apply2(::foo)) 
}
```

- 委譲プロパティの推論

1.3ではnullableな型の移譲プロパティを使う場合、型を明確に指定する必要があった。
1.4では明確に指定する必要はなくなった。

```kotlin
import kotlin.properties.Delegates

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

1.3だとこのようにする必要があった。

```kotlin
var prop: String? by Delegates.observable<String?>(null) { _, old, new ->
```

# その他言語仕様の変更

この辺りは概ね以前の[1.4に関する記事](https://blog.jetbrains.com/kotlin/2019/12/what-to-expect-in-kotlin-1-4-and-beyond/#language-features)の通りのようです。

- 名前付き引数(named)と位置指定引数(positional)を混ぜられる。
- 委譲プロパティの挙動最適化
- リストとかで末尾をカンマにできる

```kotlin
val resultList = listOf( "aaaa", "bbbb", )
```

- whenの中でbreakやcontinueを使える

```kotlin
fun foo(list: List<Int>) {
    for (i in list) {
        when (i) {
            42 -> continue
            43 -> break
            else -> println(i)
        }
    }
}

fun main() {
    foo(listOf(42, 41, 44, 43, 45))
}
```

- 末尾再起関数(tail-recursive)の挙動変更

サンプルをみている限り、引数の評価順序が変わるようなので、現行で利用している場合、結果に違いが出そうに思います。

- Custom Contractsのケース追加

具体化型パラメータ（reified generic type parameters）を利用することができる様になりました。

```kotlin
@OptIn(ExperimentalContracts::class)
inline fun <reified T> assertIsInstance(value: Any?) {
    contract {
        returns() implies (value is T)
    }

    assertTrue(value is T)
}
```

---

# おわりに
ということで、取り急ぎ、公式記事のうち、型推論などの機能追加・変更周りをみてみました。
公式記事はまだ続きがありますし、ChangeLogをみるとIDE周りの変更なども含めるとさらに多くの変更がある様なので (流石に、FIXをすべてみることはできませんが)、正式版までもう少しチェックしていこうと思います。