🤔

【Kotlin】byと=の違いってなに?

2024/10/20に公開

はじめに

自分はまだKotlin歴が浅く(1.5ヶ月弱)でアプリ開発未経験から一気にインターンまでやっていてインプットの日々を送っています。
理解が間違っているかもしれませんが、温かい目で見守って下さい。

本題

by vs =

僕個人的にはずっとこれに苦しんできました。もともとPythonを使っていたため、変数にプロパティを委譲するなんて考えたことがなく(=で全部何とかなる)ので。
こんなところで詰まりました。

NG_1
val testInput by mutableStateOf("")
testInput = "hoge"
NG_2
val testInput by mutableStateOf("")
testInput.value = "hoge"

では何故これらが弾かれてしまうのでしょうか、、、

なんやねん、プロパティ委譲て

プロパティ委譲、別名Delegateと言います。イメージ的にはカービィが相手食ってそいつの能力を使えるようになる的な(間違っていたらごめんなさい)
コード的には以下のようになる。

OK
import kotlin.reflect.KProperty

// 委譲先のクラス
class SimpleDelegate(private val initialValue: String) : ReadOnlyProperty<Any?, String> {
    private var value = initialValue
    
    // プロパティの値を取得
    override fun getValue(thisRef: Any?, property: KProperty<*>): String {
        println("Getting value for ${property.name}: $value")
        return value
    }
}

class Example {
    // SimpleDelegateを使ってnameプロパティを委譲
    val name: String by SimpleDelegate("Kotlin")
}

fun main() {
    val example = Example()
    println(example.name)  // 委譲されたプロパティを取得
    println(example.name)  // 2回目の取得
}

今回はReadOnlyPropertyのところでStringの型を定義しているので、val name:Stringにしている。
こんな感じでbyを使うと、委譲先のクラスのプロパティを使うことができてすごい便利(.valueとか使ってアクセスすることなく、直接アクセスすることができる。)

なぜさっきうまくいかなかったのか

NG1でダメだった理由はつまりvalを使ってしまったからだ。
プロパティ委譲を使った変数はvalueに直接アクセスすることができると言ったが、valは不変の変数であることを忘れてはいけない。
よって以下の例で改善することができる。

OK
var testInput by mutableStateOf("")
    private set
testInput = "hoge"

varだと可変の変数であるため、変更可能になってしまうのだ。そして、private setにすることで、クラス内部のみで変更可能にすることができる。

NG2でダメだった理由は.valueを使ってしまったからだ。
プロパティ委譲をしてしまった以上、直接アクセスすることしかできないため、もし.valueでアクセスしたい場合は以下のように修正する必要がある。

OK
val testInput = mutableStateOf("")
testInput.value = "hoge"

これだとtestInput内のmutableStateOfを変更していることになり、エラーが出ない。(さっきはtestInputそのものがmutableStateOfになってしまっている、という感覚)

参考文献

ChatGPTさん、議論に付き合ってくれてありがとう。

Discussion