Kotlin1.3 まとめ
10/30日にKotlin1.3がりりーすされました。 1.2や1.3RCからの変更点等を主にJVMで動かすときに関わる範囲で適当にまとめようと思います。
変更・追加点
kotlinx.coroutines
がexperimentalからstableにcapturing when subject
引数なしエントリポイント
main
contracts
Result<T>
の実装inline class
の実験的なサポートunsigned integer
の実験的なサポートCollection
まわりでなんかいろいろ拡張関数追加された。annotation calss
をネストして宣言できるようになった。@JvmStatic
と@JvmField
をcompanion object
内で使えるようになったBool
型がcompanion object
を持つようになった。基本型の
companion object
になんかいろいろ追加された。
適当にピックアップしてそれぞれ例を挙げて解説してみます。
引数なしエントリポイント
引数なしのmain関数がエントリポイントになるようになりました。
従来の main(args: Array<String>)
は当然なくなってないです。
初心者フレンドリらしい。
fun main() { println("はろわ!") } //はろわ!
capturing when subject
when
式に使う変数をwhen
式の中で宣言できるようになったやつ。
when (val hoge = createRandomString()) { "hoge" -> println(hoge) }
こんなかんじ
変数のスコープはwhen式の中に制限される。
Collection まわりの拡張関数
なんかいろいろ追加されてる。
Sequence
の orEmpty()
とか Collection
,Map
,List
のisNullOrEmpty()
とかはAPIの一貫性を向上させるために用意されたらしい。
val strArray = arrayOf("aa", "bbb", "ccccccc") strArray.isNullOrEmpty() //false strArray.ifEmpty { arrayOf("hoge", "hoge") } val strSequence: Sequence<String>? = strArray.asSequence() strSequence.orEmpty() requireNotNull(strSequence).ifEmpty { emptySequence() } val strList: List<String>? = strArray.toList() strList.isNullOrEmpty() // false if (!strList.isNullOrEmpty()) { //contractsのおかげでスマートキャストされるようになった。 strList.ifEmpty { listOf("hoge") } }
Result
成功した処理の結果 T
か 失敗した結果 Throwable
を格納する共用体。
早速inline class
で実装されてる。
1.3 m1
時点では SuccessOrFailure
という名前で実装され rc-190
にて Result
に名前が変わった。
自分は偶然 Result
ってtypealias
を作って使っていたので I get kotonaki でした。
基本的にはrunCatching
関数を使って生成するものだと思う。
val result: Result<String>= runCatching { randomeFailuire() //一定確率で例外投げる関数だと思って "hoge" } result.onSuccess(::println) .onFailure(Throwable::printStackTrace) fun Throwable.zero() = 0 val fold: Int = result.fold(String::length, Throwable::zero)
onSuccess
はT->Unit
を受け取りResult<T>
を返します。
成功した場合は当然それを実行。
onFailure
は Throwable->Unit
を受け取りResult<T>
を返します。
fold
は T->R
とThrowable->R
を受け取り成功した場合は前者、失敗した場合は後者を実行しR
を返す関数です。
多分よく使うのはこの3つだと思う。
他にもmap()
とかgetOrDefault()
とか便利なのいろいろ用意されてます。
ちなみにResult
を返す関数を作るには今の所コンパイラに-Xallow-result-return-type
を渡す必要があります。
inline class
primary constructor
で必ず一つの引数を受け取ってそれがコンパイル時にインライン展開されるかもしれない。
当然普通のクラスとは違っていろいろ制限がある。
primary constructor
はval
でアノテートされたプロパティを一つだけ持つ必要がある。backing field
を持つproperty
(よく見る普通のプロパティ)は定義できない。initializer block
を持てない。===
(参照等価性)での比較は不可
他にも制限あったかもしれない。
正確には必ずコンパイル時にプロパティがインライン展開されるわけではなくて、可能な限りインスタンスの生成等を行わずに済むように最適化しようとする感じです。
その最適化を行うために上記の制限があるみたい。
fun main() { val age = Age(14) val ageValue = age.value println(age) println(ageValue) println(age.birthYear()) } inline class Age(val value: Int) { fun birthYear(): LocalDateTime { //todo 雑 val now = LocalDateTime.now() return LocalDateTime.of( now.year - value, now.month, now.dayOfMonth, now.hour, now.minute ) } }
こんな感じのコードはコンパイルされると
fun main() { val age: Int = Age.constructor_impl(14) val var2: Age = Age.box_impl(age) println(var2) println(age) val var3:LocalDateTime = Age.birthYear_impl(age) println(var3) }
こんな感じになります。
クラスの実装は割愛です。
inline class
を使用するにはコンパイラに引数として-XXLanguage:+InlineClasses
を渡す必要があります。
kotlinx.coroutines
これは例とか出してるとやたら長くなりそうなので 1.0.0
での変更点を自分が実際に使った際に気づいたところを軽くまとめようと思います。
グローバルで
suspend property
なcoroutineContext
が消えてCoroutineScope
のプロパティになった。launch()
等の関数がGlobalScope
オブジェクトの拡張関数になった。launch()
やasync
からparent: Job?
の引数が消えた。async(coroutineContext + CommonPool){}
とかしてたと思うけどCommonPool
消えのでForkJoinPool.commonPool().asCoroutineDispatcher()
とかする。launch(UI){ }
とかしてたと思うけどUI
消えた。Dispatchers.Main
に置き換える
launch
と async
くらいしか使わないから気づいたこと少なすぎた…
んぐぉぉぉぉぉ……
contracts
コンパイラになんかいろいろ教えてあげるやつ。
これのおかげで特定のメソッド呼び出しの結果が true
ならレシーバがnon-null
型にスマートキャストされたりとか、ラムダ式が一度だけ同期的に呼び出されることを保証してその中で外のスコープの変数を初期化できるようになったりとかする。
早速Array<T>?.isNullOrEmpty()
とか requireNotNull()
とか T.run()
とかに使われている。
使い方の例として一度だけ呼ばれるラムダ式のcontracts
を挙げ…ようとおもったけどなんかビルド通らないな…んんん~~また今度。
1.3-m2
が出て即試したときは普通にできたけどBackend Internal error
吐きまくってなんかビルドできないので寝ます。
標準ライブラリのrequire
関数とか使う分には問題なく動きます。
以上
なんか変なこと書いてたりしてたら指摘していただければ嬉しいです。