クラスの実装 〜 プロパティとバッキングフィールド

Kotlin でプロパティを定義する場合、次のように記述できます。

class Person {
    private var _text: String = ""

    var name: String
        get() {
            return _text
        }
        set(value) {
            _text = value
        }
}

この Person クラスでは name というプロパティを公開しています。 そして、そのプロパティの値は Person 内部では _text というメンバー変数に保存しています。

このように、あるプロパティの値を保持するデータストレージ (ここではメンバー変数) のことを、バッキングフィールド (backing field) といいます。

プロパティのゲッターとセッターは次のように、プロパティ名に続き get()set(value) を書くことで記述できます。

    var name: String
        get() {
            return _text
        }
        set(value) {
            _text = value
        }

プロパティ名に続く、get() ではそのプロパティの値を返すように実装します。 また、set(value) は、外部からそのプロパティに値をセットした時に、value に自動的に値が入ります。 この値をバッキングフィールド (ここでは _text メンバ変数) にセットできます。

この Person クラスを使う側では、次のようにプロパティを使えます。

fun main() {
    val p = Person()
    p.name = "Mike"
    println(p.name) // "Mike"
}

暗黙的なバッキングフィールドの生成と field キーワード

Kotlin ではバッキングフィールドを明示的に作成しなくとも、必要に応じて自動的に生成することができます。

上と同様のコードは、Kotlin では次のようにも記述できます。

class Person {
    var name: String = ""
        get() {
            return field
        }
        set(value) {
            field = value
        }
}

どこが変わったでしょうか。

まず、バッキングフィールドとして明示的に用意した、_text メンバーがありませんね。 その代わり、field というキーワードが登場しています。これはなんでしょうか。

Kotlin では、あるプロパティを実装するために必要なバッキングフィールドは必要に応じて暗黙的に自動生成され、ゲッターとセッターで field というキーワードでそれにアクセスできるのです。

もうひとつの違いとして、プロパティ名と型に続いて、バッキングフィールドの初期化をおこおなっています。ここでは = "" として、空文字に設定しています。

バッキングフィールドを明示的に作成しなくても良いだけでも、だいぶスッキリしましたが、実は上のコードはさらに簡潔に記述できます。

このゲッター get() やセッター set(value) は、単純にバッキングフィールドの値を返したり、set(value) で受け取った値を単純に field にセットしているだけです。 このような場合は、ゲッターとセッターは省略可能です。

省略可能なものを省略すると、上と同じ Person クラスと同じ働きをするクラスは、次のようにかけます。

class Person {
    var name: String = ""
}

繰り返しになりますが、この定義だけ見ると、あたかも name という名前の変数を作り、それに外部からアクセスしているように見えますが、 実際は背後にバッキングフィールドが作成されており、それにはゲッターやセッターから field というキーワードでアクセスできる、という仕組みになっています。

ここまでお読みいただき、誠にありがとうございます。SNS 等でこの記事をシェアしていただけますと、大変励みになります。どうぞよろしくお願いします。

© 2024 Kotlin 入門