2016/12/23の30分 Kotlinメモ😏

2016/12/23の30分 Kotlinメモ

書いてるコードは雑にここにあります。

github.com

Kotlinスタートブック -新しいAndroidプログラミング を引き続き読んでる。

Kotlinスタートブック -新しいAndroidプログラミング

Kotlinスタートブック -新しいAndroidプログラミング

Extension Properties

kotlinlang.org

プロパティも拡張できるのか。便利だなーまったく!!

// 拡張プロパティ
// バッキングフィールドは持てない
val String.countWords: Int
    get() = split("""\s+""".toRegex()).size

println("Kotlin Kotlin Kotlin!!".countWords) // 3

継承

kotlinlang.org

継承していいものに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メモ🎲

書いてるコードは雑にここにあります。

github.com

Kotlinスタートブック -新しいAndroidプログラミング を引き続き読んでる。

Kotlinスタートブック -新しいAndroidプログラミング

Kotlinスタートブック -新しいAndroidプログラミング

Properties and Fields

kotlinlang.org

プロパティはあくまで窓口であり、実際に値を保持する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さんが書いてる以下を読んだら納得できた。

バッキングフィールド?なんだそれ?となったら、読んでみるとよい。

5. プロパティの正体 · Anatomy Kotlin

Late-Initialized Properties

kotlinlang.org

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メモ🍥

書いてるコードは雑にここにあります。

github.com

Kotlinスタートブック -新しいAndroidプログラミング を引き続き読んでる。

Kotlinスタートブック -新しいAndroidプログラミング

Kotlinスタートブック -新しいAndroidプログラミング

Object Expressions

kotlinlang.org

objectね!

なんか色々できるので、どんな用途で使うのがいいんだろーと悩む🤔

きっといつか答えが出るだろう...!

Interface

kotlinlang.org

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メモ👽

書いてるコードは雑にここにあります。

github.com

Kotlinスタートブック -新しいAndroidプログラミング を引き続き読んでる。

Kotlinスタートブック -新しいAndroidプログラミング

Kotlinスタートブック -新しいAndroidプログラミング

Closures

Closuresなんぞ?という話は以下でなんとなくわかる。

qiita.com

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ではない!と言われてるわけだ。

d.hatena.ne.jp

Closuresにあまり慣れていないからどういう場合に有効なのかは今後考えてみる

Inline Functions

kotlinlang.org

インライン展開することを意識して書く必要があるのね。

確かに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メモ🍭

書いてるコードは雑にここにあります。

github.com

Kotlinスタートブック -新しいAndroidプログラミング を引き続き読んでる。

Kotlinスタートブック -新しいAndroidプログラミング

Kotlinスタートブック -新しいAndroidプログラミング

Higher-Order Functions and Lambdas

kotlinlang.org

関数オブジェクトとか関数型とか高階関数とか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() }

まとめ

雑な理解で雑なコードを書いていくぞい!!