Javaを用いたオブジェクト指向プログラミングを学ぶ際、最初の一歩として避けて通れないのが「ゲッター(Getter)」と「セッター(Setter)」の概念です。

これらは単にクラス内の変数にアクセスするためのメソッドというだけではなく、Javaの核心的な設計思想である「カプセル化」を実現するための不可欠なツールです。

プログラムの規模が大きくなるにつれ、データの整合性を保ち、予期せぬ不具合を防ぐための仕組みが重要になります。

本記事では、ゲッター・セッターの基本的な書き方から、なぜそれらが必要なのかという設計上の理由、さらには最新のJava環境における効率的な自動生成手法まで、エンジニアとして知っておくべき知識を網羅的に解説します。

ゲッター・セッターとは何か

ゲッターとセッターは、クラス内のフィールド(メンバ変数)に対して、外部から値を「取得」したり「書き込み」したりするための専用メソッドです。

通常、Javaの設計原則では、クラスのフィールドは直接公開せず、アクセス修飾子を private に設定して外部からの直接的なアクセスを遮断します。

ゲッター(Getter)の役割

ゲッターは、オブジェクトが持つプライベートなフィールドの値を外部へ返すためのメソッドです。

命名規則として、フィールド名の頭に get を付け、キャメルケース(単語の区切りを大文字にする形式)で記述するのが一般的です。

例えば、フィールド名が name であれば、メソッド名は getName() となります。

セッター(Setter)の役割

セッターは、外部から受け取った値をオブジェクトのプライベートなフィールドに代入するためのメソッドです。

命名規則は、フィールド名の頭に set を付けます。

例えば、フィールド名が age であれば、メソッド名は setAge(int age) となります。

セッターの内部で、代入される値が適切かどうかをチェックする処理(バリデーション)を記述することもあります。

なぜゲッター・セッターが必要なのか:カプセル化の重要性

「わざわざメソッドを通さなくても、フィールドを public にして直接アクセスすればいいのではないか」と考える初心者の方は少なくありません。

しかし、大規模な開発やチーム開発において、フィールドの直接公開は保守性と安全性を著しく低下させる要因となります。

カプセル化による情報隠蔽

オブジェクト指向の四大要素の一つである「カプセル化(Encapsulation)」は、データとその操作を一つのユニットにまとめ、外部から直接触れられないようにすることを指します。

ゲッター・セッターを用いることで、内部の実装詳細を隠蔽し、オブジェクトの状態を常に制御下に置くことが可能になります。

不正な値の混入を防ぐ

もしフィールドが public であれば、外部のどのクラスからでも自由な値を代入できてしまいます。

例えば、人間の年齢を表す age フィールドに -5 というマイナスの数値が代入されることを防げません。

セッターを経由させることで、「値が0以上であるか」といったチェックを行い、不適切な値の入力を未然に防ぐことができます。

読み取り専用・書き込み専用の制御

フィールドを private にし、ゲッターのみを提供すれば、そのフィールドは外部から「読み取り専用」となります。

逆にセッターのみを提供すれば「書き込み専用」にできます。

このように、アクセス権限を細かく制御できる点が大きなメリットです。

変更に対する柔軟性

将来的にフィールドのデータ型を変更したり、値を返す際のロジックを変更したりする場合でも、ゲッター・セッターを使用していれば、外部のコードに影響を与えずに内部の実装を修正できます。

これを「波及効果の最小化」と呼び、保守性の高いコードを書くための重要なテクニックです。

ゲッター・セッターの基本的な書き方

それでは、実際にJavaでゲッターとセッターを実装する基本的なコード例を見ていきましょう。

基本的なクラス構成

以下の例では、従業員情報を管理する Employee クラスを作成しています。

Java
public class Employee {
    // フィールドをprivateに設定してカプセル化
    private String name;
    private int salary;

    // nameのゲッター
    public String getName() {
        return name;
    }

    // nameのセッター
    public void setName(String name) {
        this.name = name;
    }

    // salaryのゲッター
    public int getSalary() {
        return salary;
    }

    // salaryのセッター
    public void setSalary(int salary) {
        // バリデーションの例:給与が0円未満にならないように制御
        if (salary >= 0) {
            this.salary = salary;
        } else {
            System.out.println("エラー:給与に負の値は設定できません。");
        }
    }
}

実行用クラスでの使用例

作成したクラスをインスタンス化し、ゲッター・セッターを通じてデータを操作します。

Java
public class Main {
    public static void main(String[] args) {
        Employee emp = new Employee();

        // セッターを使用して値をセット
        emp.setName("山田 太郎");
        emp.setSalary(300000);

        // ゲッターを使用して値を取得し出力
        System.out.println("従業員名: " + emp.getName());
        System.out.println("給与: " + emp.getSalary() + "円");

        // 不正な値をセットしようとする試み
        emp.setSalary(-500);
    }
}
実行結果
従業員名: 山田 太郎
給与: 300000円
エラー:給与に負の値は設定できません。

上記の結果からわかるように、セッターを用いることでロジックに基づいた値の制御が行われています。

命名規則と注意点

Javaには「JavaBeans」という仕様があり、ゲッター・セッターの命名には厳格なルールが存在します。

このルールに従うことで、多くのライブラリやフレームワーク(Spring Bootなど)がオブジェクトの値を自動的に扱えるようになります。

一般的な命名ルール

項目命名規則
基本的な取得get + フィールド名getEmail()
基本的な設定set + フィールド名setEmail(String email)
boolean型の取得is + フィールド名isActive()

boolean型の特殊な扱い

Javaでは boolean 型(プリミティブ型)のフィールドに対するゲッターは、 get ではなく is を接頭辞として使うのが一般的です。

ただし、ラッパークラスである Boolean 型を使用する場合は get を使うこともあるため、プロジェクトの規約に合わせる必要があります。

this キーワードの活用

セッターの引数名とフィールド名が同じ場合、どちらを指しているかを明確にするために this キーワードを使用します。

this.name = name; と記述することで、「このクラスのインスタンス変数 name に、引数の name を代入する」という意味になります。

ゲッター・セッターの自動生成手法

プログラミングにおいて、すべてのフィールドに対して手動でゲッター・セッターを記述するのは非常に手間がかかり、コードの可読性を下げる要因にもなります。

現代の開発現場では、ツールやライブラリによる自動生成が一般的です。

IDE(統合開発環境)の機能を利用する

主要なJavaのIDE(Eclipse, IntelliJ IDEA, VS Codeなど)には、ソースコードを右クリックするだけでゲッター・セッターを自動挿入する機能が備わっています。

Eclipse

右クリック → [ソース][getterおよびsetterの生成]

IntelliJ IDEA

[Alt] + [Insert] キー → [Getter and Setter]

VS Code

右クリック → [ソースアクション][Generate Getters and Setters]

この方法であれば、数秒で数十個のメソッドを作成でき、タイポ(打ち間違い)も防ぐことができます。

Project Lombok(ロンボク)を利用する

さらにスマートな方法として、Lombok というライブラリを使用する手法があります。

アノテーション(注釈)をクラスに付与するだけで、コンパイル時にゲッター・セッターを自動的に生成してくれます。

Java
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class Product {
    private String productId;
    private String productName;
    private int price;
}

このコードだけで、コンパイル後のクラスファイルには自動的にゲッターとセッターが含まれます。

ソースコードが非常にスッキリし、本質的なロジックに集中できるため、現代のJava開発(特にSpring Bootなど)ではデファクトスタンダード(事実上の標準)となっています。

最新Javaのトレンド:Record(レコード)の活用

Java 14以降(Java 16で正式導入)では、データを保持するためだけのクラス(データ転送オブジェクト:DTOなど)をより簡単に定義するための record 構文が登場しました。

Java
public record UserRecord(String id, String name) { }

この一行だけで、不変(Immutable)なフィールド、全フィールドを受け取るコンストラクタ、そしてフィールド名と同じ名前のアクセサメソッド(ゲッター相当)が自動生成されます。

ただし、Recordのアクセサは getId() ではなく id() という名称になり、かつフィールドが final になるため、セッターは生成されません。

値の書き換えが必要な場合は従来のクラス形式を使用し、データの運搬のみが目的であれば Record を活用するという使い分けが重要です。

ゲッター・セッター使用時のベストプラクティス

単に機械的に生成するだけでなく、設計者として以下の点に注意を払うことで、より高品質なコードになります。

1. 必要以上のセッターを作らない

すべてのフィールドにセッターを付けるのではなく、不変にできるフィールドはコンストラクタで初期化し、セッターを提供しないようにします。

これにより、オブジェクトの状態が不用意に変わることを防ぎ、バグの発生率を下げることができます。

2. コレクションのゲッターに注意する

ListMap などのコレクションをゲッターで返す場合、外部でそのリストの中身を操作(addやremove)できてしまうことがあります。

カプセル化を徹底する場合、 Collections.unmodifiableList() などを使用して、読み取り専用のリストを返す工夫が必要です。

Java
public List<String> getTags() {
    // 外部からの直接編集を防ぐため、変更不可なビューを返す
    return Collections.unmodifiableList(tags);
}

3. デメテルの法則を意識する

「最小知識の原則」とも呼ばれます。

obj.getA().getB().getC() のようにゲッターを鎖のように繋いで呼び出す構造は、設計上の密結合を生み出します。

オブジェクトに「データをください」と言うのではなく、「この処理をしてください」と依頼する(Tell, Don’t Ask)設計を心がけることで、ゲッター・セッターへの依存度を適切に管理できます。

まとめ

Javaにおけるゲッターとセッターは、単なるデータの読み書き用メソッドではなく、オブジェクトの整合性を守り、保守性の高いシステムを構築するための防波堤です。

カプセル化の原則に基づき、フィールドを private に設定し、必要最小限のアクセス手段を提供することが、クリーンなコードへの第一歩となります。

また、手動での記述は最小限に留め、IDEの自動生成機能や Lombok、さらには最新の record 構文を適切に使い分けることで、開発効率を劇的に向上させることができます。

本記事で解説した役割と実装方法を正しく理解し、現場で役立つ堅牢なアプリケーション開発に役立ててください。