Kotlinのgetterとsetterについて

採用はこちら

Kotlinでは、Javaのように getX() / setX() を明示的に書くことはほとんどありません。

その理由は、Kotlinが 「プロパティ」 を言語レベルでサポートしているからです。

本記事では、

  • Kotlinのgetter / setterが何者なのか
  • いつ自動生成され、いつカスタマイズすべきか
  • バッキングフィールドの正しい理解
  • 実務でよく使われる設計パターン

正確さ重視で解説します。

目次

Kotlinでは「フィールド」ではなく「プロパティ」が基本概念

まず最も重要な前提です。

var name: String = "Taro"

この1行は、単なる変数宣言ではありません。

Kotlinではこれは プロパティ宣言 であり、以下の要素を含みます。

  • 値を保持するための バッキングフィールド
  • 値を取得する getter
  • 値を変更する setter

※ ただし、これは「この形の場合」に限った話であり、すべてのプロパティが必ずバッキングフィールドを持つわけではありません

Javaに近い形で表現すると、概念的には次のような構造になります。

private String name = "Taro";

public String getName() {
    return name;
}

public void setName(String value) {
    name = value;
}

Kotlinではこれらが自動生成されるため、通常は明示的に書く必要がありません

valvar の違い(getter / setterの有無)

宣言gettersetter再代入
valありなし不可
varありあり

val(読み取り専用プロパティ)

val age: Int = 20
  • getterのみを持つ
  • 再代入できない
  • イミュータブル設計の基本

var(変更可能なプロパティ)

var age: Int = 20
  • getter / setter 両方を持つ
  • 値の変更が可能

カスタムgetterの基本

フィールドを持たない計算プロパティ

val fullName: String
    get() = "山田 太郎"
  • バッキングフィールドを持たない
  • getterが呼ばれるたびに評価される
  • 状態ではなくロジックを表現する用途に適している

バッキングフィールドと field の正しい理解

バッキングフィールドとは?

  • プロパティの「実体」となる内部フィールド
  • すべてのプロパティに必ず存在するわけではない

field とは?

var price: Int = 1000
    get() = field
    set(value) {
        field = value
    }
  • fieldアクセサ(getter / setter)内でのみ使える特別な識別子
  • バッキングフィールドを直接参照するための手段
  • プロパティ名(price)を使うと getter / setter が呼ばれるため、無限再帰になる

NG例(無限再帰)

var price: Int = 1000
    get() = price

カスタムsetterと値のバリデーション

基本形

var age: Int = 0
    set(value) {
        field = value
    }
  • value は setter に渡される新しい値
  • 再代入はできない(読み取り専用)

バリデーションの実装例

var age: Int = 0
    set(value) {
        if (value >= 0) {
            field = value
        }
    }

不正な状態を作らせないための最重要用途

getter / setter の可視性制御

Kotlinでは setterだけを制限する という設計がよく使われます。

var balance: Int = 0
    private set
  • 外部:読み取り可能
  • 内部:変更可能

getterの可視性は通常プロパティと同じで、setterのみをより制限的にするのが一般的です。

Kotlinでは「getterを書かない」のが基本

Java的な発想で次のように書く必要はありません。

fun getName(): String {
    return name
}

Kotlinでは、単にこう書きます。

val name: String

Kotlinでは「プロパティとして表現する」こと自体が設計意図です。

バッキングフィールドを持たないプロパティの実例

var width: Int = 0
var height: Int = 0

val area: Int
    get() = width * height
  • area は値を保持しない
  • getterが呼ばれるたびに計算される
  • 状態の派生結果を安全に表現できる

「生成されない」のは getter / setter ではなく「バッキングフィールド」

誤解されやすいポイントです。

  • 計算プロパティでも getterは必ず存在する
  • 生成されないのは バッキングフィールド
val area: Int
    get() = width * height

この場合

  • getter:あり
  • バッキングフィールド:なし

Javaとの相互運用における見え方

Kotlinのプロパティは、Javaから見ると次のように見えます。

class User {
    var name: String = ""
}

⬇ Java

getName();
setName(String value);

JVMアノテーションの補足

@get:JvmName("getUserName")
var name: String = ""
  • getter名を変更できる
  • Java APIとして公開する場合のみ慎重に使用する
  • 名前衝突を起こしやすいため、設計意図が明確な場合に限定する

実務でよく使われる設計パターン

外部からは変更不可、内部でのみ制御

class User {
    var score: Int = 0
        private set

    fun addScore(point: Int) {
        score += point
    }
}
  • 状態変更を一箇所に集約
  • バグ・不正操作を防止
  • ViewModel / Domain層で頻出

getter / setter を書くべき判断基準

ケース推奨
単純な保持自動生成に任せる
値チェックが必要カスタムsetter
派生値カスタムgetter
外部から変更禁止private setter
状態+意味プロパティで表現

Kotlinらしい結論

  • getter / setter は「書く前提」のものではない
  • 必要になった瞬間にだけ追加する
  • プロパティは「状態」と「意味」を同時に表現できる
  • Javaより 安全で、簡潔で、設計意図が伝わりやすい

以上、Kotlinのgetterとsetterについてでした。

最後までお読みいただき、ありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次