覚えたら書く

IT関係のデベロッパとして日々覚えたことを書き残したいです。twitter: @yyoshikaw

Kotlin - getter/setter からのバッキングフィールドへのアクセス

Kotlin のクラスでは、フィールドを持つことができません。
しかし、カスタムアクセサを使用するときに バッキングフィールド が必要になることがあります。 この目的のために、Kotlinでは field という識別子を使用して自動的に作成されたバッキングフィールドにアクセスすることができます。


以下、商品を表現するクラスで、その商品の価格(price)を変更した場合はログ出力するようにします。

class Product(val serialNo: Int) {
    var price: Int = 10980
        set(value) {
            println("[Logging] Product price changed. [$field -> $value]")
            field = value
        }
}

price をミュータブルなプロパティで宣言して、カスタムの setter で 変更前の値と変更後の値を出力しています。

例えば以下のような実行コードが書けます

fun main() {
    val product1 = Product(100001)
    println("Product(S/N: ${product1.serialNo}) price = ${product1.price}")

    product1.price = 9980

    println("Product(S/N: ${product1.serialNo}) price = ${product1.price}")
}

実行結果は以下の通りです

Product(S/N: 100001) price -> 10980
[Logging] Product price changed. [10980 -> 9980]
Product(S/N: 100001) price -> 9980

product1.price = 9980 の 部分でプロパティの値を変更しています。
この際、内部的には setter が呼び出されています。
このクラスでは setter が再定義されていてログ出力をするようになっているので結果的に値の変更とともにログ出力も行われます。

先に書いた通りに、setterの中で、field という識別子を使ってバッキングフィールドの値にアクセスしています。


getterの場合は、値を読むことしかできませんが、setterの内部では値の読出しと変更の両方が可能です。と「Kotlinイン・アクション」には書かれていますが、

例えば以下のよなコードはコンパイルが通ります。 get() の中で field の値を書き換えできちゃってる気がするのですが、
私の理解不足なだけですかね。よくわかってない。。。

class Product(val serialNo: Int) {
    var price: Int = 10980
        set(value) {
            println("[Logging] Product price changed. [$field -> $value]")
            field = value
        }

        get() {
            field = field * 100
            return field
        }
}


まとめ

どの程度の頻度で利用することになるのかわかっていませんが、
field 識別子を利用することでカスタムアクセサの実装でバッキングフィールドにアクセスできる事がわかりました。