2016/12/23の30分 Kotlinメモ😏
2016/12/23の30分 Kotlinメモ
書いてるコードは雑にここにあります。
Kotlinスタートブック -新しいAndroidプログラミング を引き続き読んでる。
Kotlinスタートブック -新しいAndroidプログラミング
- 作者: 長澤太郎
- 出版社/メーカー: リックテレコム
- 発売日: 2016/07/13
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る
Extension Properties
プロパティも拡張できるのか。便利だなーまったく!!
// 拡張プロパティ // バッキングフィールドは持てない val String.countWords: Int get() = split("""\s+""".toRegex()).size println("Kotlin Kotlin Kotlin!!".countWords) // 3
継承
継承していいものにopen
つけろよ!ってところ以外はだいたいJavaと同じかなー。
プロパティもoverrideできるのか!?ってことには驚いた!できるのか、そうか。
open class Person(val name: String) { open var age: Int = 0 open fun introduce() { println("I am $name2.") } } class Student(name: String, val id: Long) : Person(name) { override var age: Int = 0 get() = field set(value) { field = value * 2 } override fun introduce() { println("I am $name(id=$id).") } } // super classの型にsub classの型は入る val p2: Person = Student("kotlin", 1L) p2.introduce()
Abstract Classes
これはほぼJavaと同じかなー
そしてプロパティをabstractにできるのか!?できるのか、そうか。
abstract class Greeter(val target: String) { abstract var code: String abstract val age: Int abstract fun sayHello() } class EnglishGreeter(target: String, override val age: Int) : Greeter(target) { override var code: String = "" get() = "en" set(value) { field = value } override fun sayHello() { println("Hello $target!") } } class JapaneseGreeter(target: String, override val age: Int) : Greeter(target) { override var code: String = "" get() = "ja" set(value) { field = value } override fun sayHello() { println("こんにちは、$target!") } }
まとめ
冬休みもKotlinガンバルぞい!
2016/12/22の30分 Kotlinメモ🎲
2016/12/22の30分 Kotlinメモ🎲
書いてるコードは雑にここにあります。
Kotlinスタートブック -新しいAndroidプログラミング を引き続き読んでる。
Kotlinスタートブック -新しいAndroidプログラミング
- 作者: 長澤太郎
- 出版社/メーカー: リックテレコム
- 発売日: 2016/07/13
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る
Properties and Fields
プロパティはあくまで窓口であり、実際に値を保持するBacking Fieldにはプロパティ内部でしかアクセスできないと。
Backing Field = 実際の値の参照を保持するもの?みたいな?
class Person { var name: String = "" set(value) { println("set $value") // field = バッキングフィールドを表す暗黙の変数 field = value } var age: Int = 0 }
以下のnameLength
プロパティはBacking Fieldは生成されない。
class Person { var name: String = "" set(value) { println("set $value") // field = バッキングフィールドを表す暗黙の変数 field = value } var age: Int = 0 val nameLength: Int get() = name.length }
Javaをやってて、プロパティ謎いぞ!フィールドと何が違うの!ってなったけど、@sys1yagiさんが書いてる以下を読んだら納得できた。
バッキングフィールド?なんだそれ?となったら、読んでみるとよい。
Late-Initialized Properties
Backing Fieldを持つプロパティは必ず初期化する必要があるけど、lateinit
をつければ初期化タイミングを遅らせることができるよ!ってことらしい。
使用できるプロパティに色々制限はあるので、用途は限られそう。
class Person { // 初期化のタイミングを遅らせる // varでしか使えない + non nullでしか使えない + primitiveには使えない + custom getter/setterは持てない // https://kotlinlang.org/docs/reference/properties.html#late-initialized-properties // DIとかに使える // 初期化する前にアクセスするとUninitializedPropertyAccessExceptionが起きる lateinit var humuhumu: String } val p = Person() // println(p.humuhumu) // kotlin.UninitializedPropertyAccessException: lateinit property humuhumu has not been initialized p.humuhumu = "humuhumu" println(p.humuhumu) // humuhumu
まとめ
プロパティの謎が少しずつわかってきたのでたのしいたのしい🎲
2016/12/21の30分 Kotlinメモ🍥
2016/12/21の30分 Kotlinメモ🍥
書いてるコードは雑にここにあります。
Kotlinスタートブック -新しいAndroidプログラミング を引き続き読んでる。
Kotlinスタートブック -新しいAndroidプログラミング
- 作者: 長澤太郎
- 出版社/メーカー: リックテレコム
- 発売日: 2016/07/13
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る
Object Expressions
object
ね!
なんか色々できるので、どんな用途で使うのがいいんだろーと悩む🤔
きっといつか答えが出るだろう...!
Interface
Javaと基本同じだけど、プロパティを持てることに驚いたぞ!!
interface BucketKai { fun fill() fun drainAway() fun pourTo(that: BucketKai) // interfaceはプロパティを持てる // プロパティはオブジェクトの内部にあるものではなく、境界にあるもの // 変数のように見えるが、実際のデータの持ち方は規定しない // 実装するオブジェクトの実装次第になる val capacity: Int var quantity: Int }
オブジェクト式で生成するとこんな感じで実装して使える。
fun createBucketKai(_capacity: Int): BucketKai = object : BucketKai { // プロパティにもoverrideがつく override val capacity = _capacity // プロパティは必ず初期化しないといけない override var quantity = 0 override fun fill() { quantity = capacity } override fun drainAway() { quantity = 0 } override fun pourTo(that: BucketKai) { val thatVacuity = that.capacity - that.quantity if (quantity <= thatVacuity) { that.quantity = quantity drainAway() } else { that.fill() quantity -= thatVacuity } } } val bucketKai1 = createBucketKai(7) val bucketKai2 = createBucketKai(4) bucketKai1.fill() bucketKai1.pourTo(bucketKai2) println(bucketKai1.quantity) // 3 println(bucketKai2.quantity) // 4
Class
上で使ったInterface BucketKai
をクラスで実装するとこんな感じ。
class BucketImpl(override val capacity: Int) : BucketKai { override var quantity = 0 override fun fill() { quantity = capacity } override fun drainAway() { quantity = 0 } override fun pourTo(that: BucketKai) { val thatVacuity = that.capacity - that.quantity if (quantity <= thatVacuity) { that.quantity = quantity drainAway() } else { that.fill() quantity -= thatVacuity } } }
まとめ
どの絵文字使ってないか忘れてきたぞい...🍥
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分以上やってますよ...はい...👽
2016/12/19の30分 Kotlinメモ🍭
2016/12/19の30分 Kotlinメモ🍭
書いてるコードは雑にここにあります。
Kotlinスタートブック -新しいAndroidプログラミング を引き続き読んでる。
Kotlinスタートブック -新しいAndroidプログラミング
- 作者: 長澤太郎
- 出版社/メーカー: リックテレコム
- 発売日: 2016/07/13
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る
Higher-Order Functions and Lambdas
関数オブジェクトとか関数型とか高階関数とかLambdaとかそこら辺の話。
前回読んだ時はちんぷんかんぷんだったけど、ちょっとわかってきたかも。
fun mumu(i: Int): Int { return i * 2 } // 関数オブジェクト val mumu = ::mumu println(::mumu) // 関数オブジェクトの型 // (引数の型) -> 戻り値の型 val mumu2: (Int) -> Int = ::mumu println(mumu(2))
Higher-Order Functions
関数を引数で受け取ったり、戻り値をして返すような関数を高階関数というらしい。
以下のように文字列から特定の条件にマッチした位置を返す関数があるとして、特定の条件ごとに関数を書いていくと。
fun firstK(str: String): Int { tailrec fun go(str: String, index: Int): Int = when { str.isEmpty() -> -1 str.first() == 'K' -> index else -> go(str.drop(1), index + 1) } return go(str, 0) } fun firstUpperCase(str: String): Int { tailrec fun go(str: String, index: Int): Int = when { str.isEmpty() -> -1 str.first().isUpperCase() -> index else -> go(str.drop(1), index + 1) } return go(str, 0) } println(firstK("aaaKaaa")) // 3 println(firstUpperCase("aaaAaaa")) // 3
特定の条件って部分を関数として受け取ればよくね?って話。なるほどー
// firstKとfirstUpperCaseの条件部分を関数オブジェクト化 fun first(str: String, predicate: (Char) -> Boolean): Int { tailrec fun go(str: String, index: Int): Int = when { str.isEmpty() -> -1 predicate(str.first()) -> index else -> go(str.drop(1), index + 1) } return go(str, 0) } fun firstK2(str: String): Int { fun isK(c: Char): Boolean = c == 'K' return first(str, ::isK) // 以下のように直で書いてもよい // return first(str, fun(c: Char): Boolean = c == 'K') // lambda式 // return first(str, { it == 'K' }) } fun firstUpperCase2(str: String): Int { fun isUpperCase(c: Char): Boolean = c.isUpperCase() return first(str, ::isUpperCase) // 以下のように直で書いてもよい // return first(str, fun(c: Char): Boolean = c.isUpperCase()) // 以下のように型を省略しても書ける // return first(str, fun(c) = c.isUpperCase()) // 以下のようにも書ける // return first(str, Char::isUpperCase) } println(firstK2("aaaKaaa")) // 3 println(firstUpperCase2("aaaAaaa")) // 3
Lambda
Lambdaなら一々::
とか書かなくても関数オブジェクト作れるよーって話。
it
便利!
// lambda式 // 関数オブジェクトを直接生成する val jijijiji: (Int) -> Int = { i: Int -> i * i } println(jijijiji(2)) // 4 // 型は省略できる val jijijiji2 = { i: Int -> i * i } println(jijijiji2(2)) // 4 // 引数が1つの場合、itを使える val jijijiji3: (Int) -> Int = { it * it } println(jijijiji3(2)) // 4
さらにLambdaを使って、文字列の中の最初の空白文字の位置を見つける関数を書いてみると。
fun firstWhitespace(str: String): Int = first(str, { it.isWhitespace() }) // 最後の引数がlambdaを取るような関数なら、以下にようにも書ける fun firstWhitespace2(str: String): Int = first(str) { it.isWhitespace() }
まとめ
雑な理解で雑なコードを書いていくぞい!!