2016/12/20の30分 Kotlinメモ👽
2016/12/20の30分 Kotlinメモ👽
書いてるコードは雑にここにあります。
Kotlinスタートブック -新しいAndroidプログラミング を引き続き読んでる。
Kotlinスタートブック -新しいAndroidプログラミング
- 作者: 長澤太郎
- 出版社/メーカー: リックテレコム
- 発売日: 2016/07/13
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る
Closures
Closuresなんぞ?という話は以下でなんとなくわかる。
getCounterメソッド内で宣言してるcount
変数をreturnのLambdaの中で参照・変更できている。
要はLambdaの外側の関数で宣言されている変数の参照・変更ができる。
以下ではgetCounterを呼び出すごとに値がインクリメントされてるので、状態を保持することができる関数とも言えるのかな?
fun getCounter(): () -> Int { var count = 0 return { count++ } } println(counter()) // 0 println(counter()) // 1 println(counter()) // 2
Javaだと参照はできるけど、変更はできない。
var sum = 0 (1..10).forEach { sum += it } println(sum) // Java int sum = 0; Stream.of(1, 2, 3) .forEach(integer -> { // Lambdaから外の変数の変更はできない sum += integer; }); public Supplier<Integer> getCounter() { int count = 0; return () -> { // Lambdaから外の変数の参照は可能 return count; }; }
うむ。なのでJavaのLambdaはClosuresではない!と言われてるわけだ。
Closuresにあまり慣れていないからどういう場合に有効なのかは今後考えてみる
Inline Functions
インライン展開することを意識して書く必要があるのね。
確かにKotlinの標準ライブラリはめっちゃinline
付いてるなーと思ってたけど理由がなんとなくわかった。
// インライン関数 // 引数の関数オブジェクトがコンパイル時にインライン展開される inline fun log(debug: Boolean = true, message: () -> String) { if (debug) { println(message()) } } log { "debug log" } log(false) { "debug log" }
crossinline
とかもいくつか使われてるの見たことあるなー
Non-local returns
Kotlin本では非ローカルリターンとラベルへのリターンと書いてある。
Lambdaの中でreturnすると関数から抜けるのね。
inline fun stringForEach(str: String, f: (Char) -> Unit) { for (c in str) f(c) } fun containsDigit(str: String): Boolean { stringForEach(str) { if (it.isDigit()) { println("containsDigit true") return true } } println("containsDigit false") return false } containsDigit("aaaaa") // containsDigit false containsDigit("aaa1aa") // containsDigit true
Lambda自身から抜けたい時はラベルへのリターンを使えってことね。
ラベル名は自分でも付けられるけど、推論できる場合は関数名を指定できるらしい。
さっきのコードは結果がどうだったかを出力するのに2箇所にpirntln書いてたけど、ラベルへのリターンを使えば出力を1箇所に集約できる。
fun containsDigit2(str: String): Boolean { var result = false stringForEach(str) { if (it.isDigit()) { result = true return@stringForEach } } println("containsDigit $result") return result } containsDigit2("aaaaa") // containsDigit false containsDigit2("aa1aaa") // containsDigit true
ラベルを自分でつけるならこんな感じ。
fun containsDigit3(str: String): Boolean { var result = false stringForEach(str) here@ { if (it.isDigit()) { result = true return@here } } println("containsDigit $result") return result }
まとめ
30分以上やってますよ...はい...👽