カテゴリ: Javaのカプセル化 更新日: 2026/08/10

Javaの不変クラス(Immutableクラス)の作り方を完全ガイド!初心者でもわかる設計手法

148
Java の不変クラス(Immutable クラス)を作る方法

先生と生徒の会話形式で理解しよう

生徒

「先生、Javaで一度作ったオブジェクトの中身を変更できないようにする方法はありますか?」

先生

「はい、Javaでは不変クラス(Immutableクラス)を作成することで、オブジェクトの状態を変更できないようにできます。」

生徒

「具体的にはどうやって作るんですか?」

先生

「それでは、基本的な作り方を見ていきましょう!」

1. 不変クラス(Immutableクラス)とは?

1. 不変クラス(Immutableクラス)とは?
1. 不変クラス(Immutableクラス)とは?

Javaにおける不変クラス(Immutableクラス)とは、一度インスタンスを生成すると、その状態(フィールドの値)を変更できないクラスのことを指します。代表的な例として、Stringクラスがあります。Stringオブジェクトは生成後にその内容を変更することができません。

不変クラスを使用することで、以下のようなメリットがあります:

  • スレッドセーフ:状態が変わらないため、複数のスレッドから同時にアクセスしても安全です。
  • 予測可能性:オブジェクトの状態が変わらないため、バグの原因となる予期しない変更を防げます。
  • セキュリティ:外部からの不正な変更を防ぐことができます。

2. 不変クラスを作成するための基本ルール

2. 不変クラスを作成するための基本ルール
2. 不変クラスを作成するための基本ルール

Javaで不変クラスを作成するためには、以下のルールを守る必要があります:

  1. クラスをfinalとして宣言する:クラスの継承を禁止し、サブクラスによる状態変更を防ぎます。
  2. すべてのフィールドをprivate finalとして宣言する:外部からの直接アクセスと再代入を防ぎます。
  3. セッターメソッドを提供しない:フィールドの値を変更する手段を与えません。
  4. コンストラクタで全てのフィールドを初期化する:オブジェクト生成時に完全な状態を設定します。
  5. ミュータブルなオブジェクトをフィールドに持つ場合、防御的コピーを行う:外部からの変更を防ぐために、コピーを作成して保持します。

3. 不変クラスの基本的な実装例

3. 不変クラスの基本的な実装例
3. 不変クラスの基本的な実装例

以下は、Personクラスを不変クラスとして実装した例です。


public final class Person {
    private final String name;
    private final int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

このクラスでは、nameageのフィールドがprivate finalとして宣言されており、セッターメソッドが存在しないため、オブジェクトの状態を変更することができません。

Javaをこれから始める人や、 オブジェクト指向の考え方を基礎から理解したい人には、 定番の入門書がこちらです。

スッキリわかるJava入門 第5版をAmazonで見る

※ Amazon広告リンク

4. ミュータブルなオブジェクトを含む場合の対処法

4. ミュータブルなオブジェクトを含む場合の対処法
4. ミュータブルなオブジェクトを含む場合の対処法

不変クラスのフィールドにDateListなどのミュータブルなオブジェクトを含む場合、外部からの変更を防ぐために防御的コピー(ディフェンシブコピー)を行う必要があります。

以下は、Dateオブジェクトを含む不変クラスの例です。


import java.util.Date;

public final class Event {
    private final String title;
    private final Date date;

    public Event(String title, Date date) {
        this.title = title;
        this.date = new Date(date.getTime()); // 防御的コピー
    }

    public String getTitle() {
        return title;
    }

    public Date getDate() {
        return new Date(date.getTime()); // 防御的コピー
    }
}

このように、コンストラクタとゲッターメソッドで防御的コピーを行うことで、外部からのDateオブジェクトの変更がEventオブジェクトに影響を与えることを防ぎます。

5. コレクションを含む不変クラスの実装例

5. コレクションを含む不変クラスの実装例
5. コレクションを含む不変クラスの実装例

コレクション(ListMapなど)をフィールドに持つ場合も、防御的コピーを行う必要があります。以下は、StudentクラスがList<String>coursesフィールドを持つ例です。


import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public final class Student {
    private final String name;
    private final List<String> courses;

    public Student(String name, List<String> courses) {
        this.name = name;
        this.courses = new ArrayList<>(courses); // 防御的コピー
    }

    public String getName() {
        return name;
    }

    public List<String> getCourses() {
        return Collections.unmodifiableList(courses); // 不変のリストを返す
    }
}

この実装では、コンストラクタでcoursesリストのコピーを作成し、ゲッターメソッドでは不変のリストを返すことで、外部からの変更を防いでいます。

6. Java 14以降のrecordを使った不変クラスの作成

6. Java 14以降のrecordを使った不変クラスの作成
6. Java 14以降のrecordを使った不変クラスの作成

Java 14以降では、recordキーワードを使用することで、簡潔に不変クラスを作成できます。recordは以下の特徴を持ちます:

  • finalクラスとして自動的に定義される
  • すべてのフィールドがprivate finalとして定義される
  • コンストラクタ、ゲッター、equals()hashCode()toString()メソッドが自動生成される

以下は、Personクラスをrecordで定義した例です。


public record Person(String name, int age) {
}

このように、recordを使用することで、ボイラープレートコードを減らし、簡潔に不変クラスを定義できます。

7. 不変クラスの活用例とメリット

7. 不変クラスの活用例とメリット
7. 不変クラスの活用例とメリット

不変クラスは、以下のような場面で活用されます:

  • マルチスレッド環境:スレッドセーフなオブジェクトとして使用できます。
  • キャッシュのキー:状態が変わらないため、ハッシュコードが一定であり、キャッシュのキーとして適しています。
  • セキュリティ:外部からの不正な変更を防ぐことができます。

また、不変クラスを使用することで、コードの可読性と保守性が向上し、バグの発生を抑えることができます。

この記事を読んだ人からの質問

この記事を読んだ人からの質問
この記事を読んだ人からの質問

プログラミング初心者からのよくある疑問/質問を解決します

Javaの不変クラス(Immutableクラス)とは具体的にどのようなクラスのことを指すのでしょうか?プログラミング初心者にもわかりやすく教えてください。

Javaにおける不変クラス(Immutableクラス)とは、一度インスタンス(オブジェクト)を生成した後に、その内部の状態や保持しているフィールドの値を一切変更することができないように設計されたクラスのことです。通常、プログラムの中で変数の値を書き換えることは一般的ですが、不変クラスで作られたオブジェクトは「作成時の状態が最後まで維持される」という特徴を持っています。Javaの標準ライブラリで最も身近な例はStringクラスであり、文字列オブジェクトを作成した後にその中身を直接書き換えることはできない仕組みになっています。このように、状態が変わらないことを保証することで、プログラムの安定性を高める役割を持っています。

不変クラスを作る際に、クラス自体を「final」として宣言しなければならないのはなぜですか?継承と関係があるのでしょうか?

不変クラスを設計する際にクラスを「final」宣言する最大の理由は、サブクラス(子クラス)による継承を禁止するためです。もしクラスが継承可能である場合、悪意のあるプログラミングや予期せぬ実装によって、サブクラス側でメソッドをオーバーライド(再定義)され、オブジェクトの状態を変更するような動作を追加されてしまう危険性があります。不変性は「絶対に変わらない」という信頼の上で成り立つため、クラスそのものをfinalにして拡張を封じることで、外部の設計者が勝手に不変のルールを破ることを防ぎ、セキュリティと整合性を守る必要があるのです。
Javaのカプセル化の一覧へ
新着記事
AWS
【AWS】CloudWatchで障害検知を自動化する設計パターン
【AWS】CloudWatchで障害検知を自動化する設計パターンを初心者向けに解説!AWS監視とアラート通知の基本
AWS
【AWS】RDSの無料枠とは?対象インスタンスタイプと注意点を解説
【AWS】RDSの無料枠とは?対象インスタンスタイプと注意点を解説
Flutter
Flutterの入力フォーム作成!TextField・TextFormFieldの基本
Flutterの入力フォーム作成!TextField・TextFormFieldの基本を初心者向けに解説
AWS
IAM Identity Centerの料金・SSO対応・Entra ID連携を詳しく解説
IAM Identity Centerの料金・SSO対応・Entra ID連携を詳しく解説【初心者向けAWS IAM】
人気記事
Flutter
Flutterの開発に必要なもの!パソコン・ツール・OSなど準備まとめ
Flutterの開発に必要なもの!パソコン・ツール・OSなど準備まとめ
Java
Java の Random クラスを使ってランダムな数値を生成する方法
JavaのRandomクラスの使い方を完全ガイド!初心者でもわかる乱数生成
Java
Java の getter メソッドと setter メソッドの使い方
Javaのgetterメソッドとsetterメソッドの使い方を完全ガイド!初心者でもわかるアクセス方法
AWS
【AWS】RDSの料金体系まとめ!ざっくり理解・高いと感じる理由も解説
【AWS】RDSの料金体系まとめ!ざっくり理解・高いと感じる理由も解説

🔌 USBポート不足を解消

Type-C 1本で拡張。
開発・作業環境を一気に快適に

UGREEN USB-Cハブを見る

※ Amazon広告リンク