この記法の意味と、以下のような import 文を記述しなければならない理由がわからなかったので、これらの意味を調べていく。
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
var ... by ... とは
委譲プロパティと呼ばれるもの。この構文を使うためには、Foo クラスに getValue(Nothing?, KProperty<*>): T と setValue(Nothing?, KProperty<*>, value: T) を実装する必要がある。
import kotlin.reflect.KProperty
class Foo {
operator fun getValue(a: Nothing?, b: KProperty<*>): Int {
return 1
}
operator fun setValue(a: Nothing?, b: KProperty<*>, value: Int) {
}
}
fun main() {
var foo by Foo()
println(foo) //=> 1
foo = 2
println(foo) //=> 1
}
foo は Int 型にも関わらず、式評価時と代入時には getValue() と setValue() が実行される。プロパティに似ている。
まとめると、次のような構造をしていることがわかる。
var foo/* 変数名 */ by Foo()/* getValueとsetValueを持つオブジェクト */
var count by remember { mutableStateOf(0) } とは
これを var count by remember { mutableStateOf(0) } に当てはめる。
-
count: 変数名 -
remember { mutableStateOf(0) }:getValueとsetValueを持つオブジェクト
remember { mutableStateOf(0) } は MutableState<Int> 型のインスタンスを返す関数の呼び出しである。MutableState<Int> とそのインタフェースである State<T> はそれぞれ次のように定義されている。
package androidx.compose.runtime
@Stable
interface State<out T> {
val value: T
}
@Stable
interface MutableState<T> : State<T> {
override var value: T
operator fun component1(): T
operator fun component2(): (T) -> Unit
}
var count by remember { ... } といった記述を行うために必要な getValue と setValue の定義が見当たらない。これらのメソッドは拡張関数として定義されている。
package androidx.compose.runtime
@Suppress("NOTHING_TO_INLINE")
inline operator fun <T> State<T>.getValue(thisObj: Any?, property: KProperty<*>): T = value
@Suppress("NOTHING_TO_INLINE")
inline operator fun <T> MutableState<T>.setValue(thisObj: Any?, property: KProperty<*>, value: T) {
this.value = value
}
よって、var count by remember { ... } と書くためには次の import 文を追加する必要がある。
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
まとめ
-
var A by Bという記法は委譲プロパティと呼ばれるもので、BはgetValueとsetValueを持つオブジェクトでなければならない -
remember { mutableStateOf(0) }はMutableState<T>型のインスタンスを返すが、この型とそのインタフェースであるState<T>自体にはgetValueとsetValueの定義が含まれていない。これらは拡張関数として定義されており、androidx.compose.runtime.getValueとandroidx.compose.runtime.setValueをインポートすることで使えるようになる