Kotlin / Programming

ザクっと理解するKotlin properties

プロパティとは

Javaで言うところのフィールドとゲッターとセッターメソッドに相当する機能です。

クラス内で宣言された変数(フィールド)に対してゲッターとセッターを宣言する。

これを1度に全てできるのがプロパティ(の宣言)です。

プロパティの宣言

Kotlinでは、varキーワードをつけることで再代入可能な、valキーワードをつけることで再代入不可能なプロパティを宣言するできます。

class Person {
    val firstName: String = "Taro"	// 再代入不可
    var lastName: String = "Sato"	// 再代入可
    var age: Int = 40			// 再代入可
}

プロパティ名を書くだけで値を使用できます。

fun main() {
    val person = Person()		// 再代入不可
    println(person.firstName)
    // 実行結果
    // Taro
}

class Person {
    val firstName: String = "Taro"	// 再代入不可
    var lastName: String = "Sato"	// 再代入可
    var age: Int = 40			// 再代入可
}

ちなみに再代入不可のvalに再代入を試みると下記エラーが表示されます。

Val cannot be reassigned

訳: valで宣言したものは再代入できません。

ゲッターとセッター

プロパティを宣言する完全な構文は以下の通りです。

var <propertyName>[: <PropertyType>] [= <property_initializer>]
    [<getter>]
    [<setter>]

初期化、ゲッター(getter)、セッター(setter)は任意です。

初期化、もしくはゲッターの戻り値の型から型が推測できる場合はプロパティの型指定は不要です。

var name = "propertyName"  // Stringと型が推測できる
var age                    // 名前からIntが入りそうだが実際はわからない → 型が推測できないためエラー

再代入不可能プロパティ(val)は再代入可能プロパティ(var)と2つの点で異なります。

・頭にvarではなくvalをつける

・セッターを保持していない

val name = "propertyName"  // String型でありゲッターのみ保持している

ゲッターやセッターを変更することもできます。

ゲッターを変更した場合、呼び出されるたびにそのゲッターを使用します。

fun main() {
    val person = Person()
    println(person.firstName)
    // 実行結果
    // My name is Ken
}

class Person {
    val firstName
    	get() = "My name is Ken"
}

ここで下記のようにfirstNameを初期化していると赤字のエラーが表示されます。

class Person {
    val firstName = "Ken" // ここでエラー
    	get() = "My name is Ken"
}

Initializer is not allowed here because this property has no backing field

訳: このプロパティにはバッキングフィールドがないため、ここで初期化はできません。

解決方法はvalをvarに変更するか、初期化をしないか(= “Ken”を消去する)になります。
※バッキングフィールドに関してはにあります。

カスタムセッターも同様にプロパティに値を代入する際に毎回呼び出されます。

ただし、プロパティの初期化時には呼び出されません。

fun main() {
    val person = Person()
    println(person.count) // 0
    
    person.count = 5
    println(person.count) // 10
}

class Person {
    var count = 0
    	set(value) {
            field = value * 2
        }
}

カスタムセッターを指定し、渡された値を2倍にして変数に格納しています。
※fieldについてはにあります。

ゲッターやセッターに注釈をつける必要や可視性(publicやprivateなどのこと)を変更する必要があるが、実装内容を変更したくない場合は、ボディを指定せずにゲッターやセッターを設定できます。

fun main() {
    val person = Person()
    println(person.count)
    
    // countのセッターの可視性がprivateのためエラー
    // person.count = 5
    
    person.changeCountValue(5)
    println(person.count)
}

class Person {
    var count = 0
    	private set
    
    fun changeCountValue(value: Int) {
        count = value
    }
}

バッキングフィールドとバッキングプロパティ

こちらにまとめてます。

コンパイル時定数

もしコンパイル時に読み取り専用とわかっている値がある場合は、constキーワードを使ってその値をコンパイル時定数としてマークしてください。

コンパイル時定数としてマークできるのは以下をすべて満たすプロパティのみです。

・トップレベルプロパティか、オブジェクト宣言のメンバーか、コンパニオンオブジェクトのメンバー

・String型かプリミティブ型の値で初期化されている

・カスタムゲッターを設定されていない

コンパイラは、定数の使用箇所をインライン化し、その参照を実際の値で置き換えます。

ただし、フィールドは削除されないため、リフレクションを使用してそれにアクセスすることができます。

※インライン化については別記事を作成中!

※リフレクションについては別記事を作成中!

コメント

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です