13
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Kotlin 1.3 まとめ(Coroutines、Kotlin/Native以外の変更点)

Last updated at Posted at 2018-11-01

はじめに

Kotlin 1.3が 2018/10/30 にリリースされました
ここでは以下に記載の変更点をメインでまとめていきます(全部は網羅していません)

実際のサンプルコードがあるところの再生ボタンを押すとそのコードが実行されて結果が参照できます
便利なので試してみてください!

注意点

基本的には上記の公式リファレンスを和訳しつつ、各種参考サイトを利用させていただきながらまとめています
英語力があまり自信ないので、内容に不備等があるかもしれませんがご容赦ください
むしろご指摘いただけるとうれしいです!

また、注目のCoroutinesやKotlin/Nativeに関してはまとめていません。
それだけでかなりのボリュームになりますので、別の投稿などでまとめていければと思ってはいます。
(予定は決まってないですが。。。)

Kotlinのmigration tool

今回は機能が多いためか、Migration Toolが提供されています

Tools > Kotlin > Enable migrations detection (experimental)

主な機能

公式ブログで一通り紹介されています

やはり、注目はCoroutines、Kotlin/Nativeでしょうか
今回はこの中の「その他の改善」に入っているような内容を詳しく書きたいと思います

変更点

Kotlin 1.3から変わる点についてまとめます

Coroutines

正式にリリースされたことでパッケージ名が変更されたようです

  • 変更前
import kotlinx.coroutines.experimental.*
  • Kotlin 1.3
import kotlinx.coroutines.*

Contracts

便利な機能としてスマートキャストがあります

fun foo(s: String?) {
    if (s != null) s.length // Compiler automatically casts 's' to 'String'
}

ただし、チェックが別の関数などに分離されるとスマートキャストされません

fun String?.isNotNull(): Boolean = this != null

fun foo(s: String?) {
    if (s.isNotNull()) s.length // No smartcast
}

こういった課題を解決するためにContractsという機能が追加されました
Contractsは以下の2ケースでサポートされています

  • 関数の戻り値と引数の関係を宣言することで、スマートキャストを改善する

    fun require(condition: Boolean) {
        // これはコンパイラに以下のような指示をするための構文です:
        // 「この関数が正常に成功するときは、引数のconditioinはtrueです」
        contract { returns() implies condition }
        if (!condition) throw IllegalArgumentException(...)
    }
    
    fun foo(s: String?) {
        require(s is String)
        // ここで「s」はStringにスマートキャストされます
        // できないときはrequireから例外がスローされます
    }
    
  • 高階関数が存在する場合の変数の初期化解析の改善

    fun synchronize(lock: Any?, block: () -> Unit) {
        // 「この関数はblockを一度だけ呼び出します」とコンパイラに指示します
        contract { callsInPlace(block, EXACTLY_ONCE) }
    }
    
    fun foo() {
        val x: Int
        synchronize(lock) {
            x = 42  // コンパイラは、synchronizeに渡されたラムダが1回だけ呼び出されることを知っているので、
                    // 再割り当ては報告されません
        }
        println(x)  // コンパイラはラムダが確実に呼び出され、初期化されることを知っているので
                    // 「x」は初期化されるとみなされます
    }
    

Contracts in stdlib

stdlibはすでにContractsが導入されているので、追加のオプトインをすることなくすぐに恩恵が受けられます
Kotlin1.3からは以下のメソッドにContractsが追加されています

  • run, with, apply, also, let, takeIf, takeUnless, synchronized
    • ラムダの引数が1度呼び出される
  • repeat
    • ラムダの引数が何回か呼び出される
  • assertTrue, require, check
    • 引数がtrueである
  • assertFalse
    • 引数がfalseである
  • isNullOrEmpty, isNullOrBlank, assertNotNull, checkNotNull, requireNotNull
    • 引数はnullではない

Custom Contracts

カスタムでContracts宣言をする機能はexperimentalで提供されています
現時点での構文は early prototype なので変更される可能性があります

when

whenでローカル変数を定義できます
whenで利用するローカル変数のスコープがより正確になります

fun Request.getBody() =
        when (val response = executeRequest()) {
            is Success -> response.body
            is HttpError -> throw HttpException(response.status)
        }

@JvmStatic, @JvmField

interfacecompanion objectメンバーに@JvmField, @JvmStaticアノテーションを付与できるようになりました
ただし、interfaceにstatic変数、メソッドを持てるのがJava1.8からなので、もし使う場合はJVM targetを1.8にする必要があります

Nested declarations in annotation classes

annotation classenumcompanion objectをネスト出来るようになりました
具体的には以下のようなコードです

annotation class Foo {
    enum class Direction { UP, DOWN, LEFT, RIGHT }
    
    annotation class Bar

    companion object {
        fun foo(): Int = 42
        val bar: Int = 42
    }
}

Parameterless main

Androidはあまり関係ありませんが、main関数の引数が不要になりました

fun main() {
    println("Hello, world!")
}

Functions with big arity

今までは関数の引数は、最大22個という制限がありましたが、Kotlin1.3からはより多くの引数がとれるようになりました
特に個数が明記されてないですが、普通に作る上で不都合はないのではないかと思います

Progressive mode

プログレッシブモードを有効にすると、コードの安全性と正確性が向上するコンパイラでコンパイルできるようになります
重要な特性としては以下の2つがあります

  • プログレッシブモードが有効な状態でコンパイルできるコードは無効にしてもコンパイルできることを保証している
  • 基本的にはよりコードを安全にするだけである
    • 例:一部の不正なスマートキャストを禁止する、生成されたコードの動作をより安定に変更する

experimental

Kotlin1.3からexperimentalに導入されている機能を紹介します

Inline classes

inline classはプロパティや関数の宣言ができますが、以下のような制約があります

  • プライマリコンストラクタで初期化される単一のプロパティのみ宣言できる
  • initブロックは持てない
  • 内部クラスは持てない
  • バッキングフィールドを持てない
    • 単純な計算可能なプロパティ(lateinit / delegatedプロパティなし)のみを持つことができます
  • インターフェースを実装することはできるが、クラス階層に参加することはできない
inline class Name(val s: String) {
    val length: Int
        get() = s.length

    fun greet() {
        println("Hello, $s")
    }
}    

fun main() {
    val name = Name("Kotlin")
    name.greet() // greet()はstatic関数として呼ばれます
    println(name.length) // static関数としてgetterが呼ばれます
}

メインの用途としては、ビジネスロジックの層でプリミティブな型ラップする時に使うようなイメージでしょうか。

Unsigned integers

符号なし整数型が導入されています

  • kotlin.UByte:符号なし8ビット整数。範囲は0〜255
  • kotlin.UShort:符号なし16ビット整数、範囲は0〜65535
  • kotlin.UInt:符号なし32ビット整数、範囲は0〜2 ^ 32 - 1
  • kotlin.ULong:符号なし64ビット整数、範囲は0〜2 ^ 64 - 1

@JvmDefault

Java1.8のinterfaceにdefault関数が定義できるようになっています

Standard library

Multiplatform Random

利用できる全てのプラットフォームで乱数の生成方法が統一されました

import kotlin.random.Random

fun main() {
    val number = Random.nextInt(42)  // number is in range [0, limit)
    println(number)
}

isNullOrEmpty/orEmpty extensions

CharSequenceなどにはすでに導入されていますが、Collection, Map, Arrayにも導入されました

Copying elements between two existing arrays

array.copyInto(targetArray, targetOffset, startIndex, endIndex)で要素のコピーができます

val sourceArr = arrayOf("k", "o", "t", "l", "i", "n")
val targetArr = sourceArr.copyInto(arrayOfNulls<String>(6), 3, startIndex = 3, endIndex = 6)
println(targetArr.contentToString())

sourceArr.copyInto(targetArr, startIndex = 0, endIndex = 3)
println(targetArr.contentToString())

associateWith

keyのリストから値を関連づけてマップを作成する際に利用します

val keys = 'a'..'f'
val map = keys.associateWith { it.toString().repeat(5).capitalize() }
map.forEach { println(it) }

ifEmpty and ifBlank functions

Collections, maps, 配列などにifEmpty, ifBlankが追加されました
空の場合に代わりに使用できる値が指定できます

fun printAllUppercase(data: List<String>) {
    val result = data
    .filter { it.all { c -> c.isUpperCase() } }
        .ifEmpty { listOf("<no uppercase>") }
    result.forEach { println(it) }
}

printAllUppercase(listOf("foo", "Bar"))
printAllUppercase(listOf("FOO", "BAR"))

Sealed classes in reflection

KotlinのReflectionにsealed classのサブクラスを列挙することができるAPIが追加されました

まとめ

コードが正確かつ安全に書ける機能が多く追加されている印象ですね。
個人的にはスマートキャスト周りの改善やwhenで変数持てるのが特にうれしいです。
ますますKotlinでの開発がしやすくなっていきそうです。

参考

13
9
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?