Kotlinの標準ライブラリ便利ですよね。Kotlinを書き始めて T.?let{ }
の便利さには感動しました。標準ライブラリ色々ありますが、どんなものがあり、どういう挙動をするのかよく把握していなかったので、自分の勉強のために*1まとめてみました。
前提
以下のようなシンプルなクラスを標準ライブラリで触ります。
class Test { var message = "test" val length: Int get() = message.length fun foo(text: String): Unit = println("foo: " + text) }
let
定番のやつ。自身を引数としてブロック関数に渡します。変数スコープを限定したいケースで有用ですね。ブロック関数の返り値を設定してない場合は返り値がUnitになります。
// let // public inline fun <T, R> T.let(block: (T) -> R): R = block(this) val result = test.let { println("let: " + it.length) // 4 123 // return Unit when nothing is set } println("${result::class}") //kotlin.Int println("result 1: " + result) //123
T?.let { } で Nullじゃない場合に何かする、っていうのは頻出ですね。
with
これも頻出。オブジェクトがもっている関数をスコープ内で呼ぶことができる関数。AndroidだとViewの設定なんかで重宝しますね。これも実は結果が返ります。
// with // public inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block() val result2 = with(test) { foo("buz") } println("result 2: " + resultX) // Unit
run
runはletとwithが合わさったような関数です。内部でオブジェクト名をつけずに関数を呼べて、結果も返ります。同じオブジェクトを使って複数の処理を行いたい場合に綺麗にかけそうですね。
// run // public inline fun <T, R> T.run(block: T.() -> R): R = block() val result3 = test.run { println("run: " + length) 456 } println("result 3: " + result3) // 456
apply
自分自身をレシーバーとしてブロック関数を実行します。letとはことなりオブジェクト自身が返ります。オブジェクトの値を変更したものを渡したい場合に有効です。オブジェクトそのものが書き換わってしまうのでちょっと注意は必要です。
// apply // public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this } println("result: " + test.message) // test val result4 = test.apply { message = "applied" } println("result 4: " + result4.message) // applied println("result 5: " + test.message) // applied
Kotlin 1.1以降に追加されたもの
以下は1.1以降で追加された機能です。
also
自身を引数としてブロック処理を行い、自身を返します。applyのlet版といったところでしょうか。applyでこと足りそうだし、使う用途あまりなさそう?
// also //public inline fun <T> T.also(block: (T) -> Unit): T { block(this); return this } val result6 = test.also { it.foo("qux") } println("result 6: " + result6) // Test@xxx
takeIf
自身を引数とした述語の結果をうけて自身かnullを返します。applayとかとつなげると有用そうですね。
// takeIf // public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? = if (predicate(this)) this else null val result7 = test.takeIf{ it.length == 4 // true } println("result 7: " + result7?.message) // test val result8 = test.takeIf{ it.length != 4 // false } println("result 8: " + result8?.message) // null
takeUnless
takeIfの逆バージョン。
// takeUnless // public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? = if (!predicate(this)) this else null val result9 = test.takeUnless{ it.length != 4 } println("result 9: " + result9?.message) // test
repeat
他のとは異なりただ実行するのみの関数です。指定した回数を引数として与えた関数を繰り返します。
repeat(3, { test.foo(it.toString()) }) //foo: 0, foo: 1, foo: 2
以上です。
参考リンク
http://beust.com/weblog/2015/10/30/exploring-the-kotlin-standard-library/ 上の例の多くは網羅されてます。ただ情報が古くて一部仕様が変わっているのと、Kotlin1.1の内容は記載ないです。
https://github.com/JetBrains/kotlin/blob/master/libraries/stdlib/src/kotlin/util/Standard.kt 主にここを見ながら Try Kotlinで実際に動かして確認しました。
*1:多分Qiitaにはまとめてあるのだろうけど