カプセル化とは?オブジェクト指向の基本とJava実装をわかりやすく解説
カプセル化とは、データ(フィールド)とそれを扱う操作(メソッド)を一つのまとまりにして、内部を外部から直接触れないように隠す設計手法です。オブジェクト指向プログラミングでは継承・ポリモーフィズムと並ぶ三大要素の一つで、クラスの内部構造を隠して「使い方(インターフェース)」だけを公開することで、想定外のデータ変更を防ぎ、変更に強いコードを保ちます。この記事では、カプセル化の定義とメリット、Javaでのprivateとgetter/setterの書き方、アクセス修飾子、情報隠蔽との違い、そして初心者がつまずく破綻パターンまでを具体例で整理します。
まとめ:カプセル化の要点
- カプセル化=データと操作を一つにまとめ、内部を隠して公開範囲を絞る設計。オブジェクト指向の三大要素の一つ。
- Javaではフィールドを
privateにし、必要な操作だけをpublicのgetter/setterで提供するのが基本形。 - 目的は「不正・想定外の変更を防ぐ」「内部実装を後から自由に差し替える」こと。単なる情報の隠しではなく変更容易性のための仕組み。
- 情報隠蔽=設計方針、データ隠蔽=その実装手段、カプセル化=両者をまとめて指す概念、という関係。
- getter/setterを全フィールドに機械生成するのはカプセル化になっていない。可変オブジェクトの参照漏れにも注意。
カプセル化とは何か|データと操作をまとめ外部から隠す
カプセル化を一言でいうと「関連するデータと処理を一つのクラスにまとめ、外から直接いじれないようにする」ことです。オブジェクトの利用者は内部がどう実装されているかを知らなくても、公開されたメソッドを呼ぶだけで目的を果たせます。内部を隠すことで、後から実装を変えても利用側のコードを壊さずに済みます。
オブジェクト指向の三大要素としての位置づけ
オブジェクト指向の三大要素は、カプセル化・継承・ポリモーフィズムです。カプセル化が「一つのオブジェクトの内部をどう守るか」を担うのに対し、継承は「既存クラスの性質を引き継いで拡張する」、ポリモーフィズムは「同じ呼び出しでオブジェクトごとに異なる振る舞いをさせる」役割を持ちます。三者は独立ではなく、しっかりカプセル化されたクラスほど、継承やポリモーフィズムで安全に組み合わせられます。なお、これに抽象化を加えて「四大原則」と数える流儀もありますが、カプセル化・継承・ポリモーフィズムを三大要素とするのが一般的です。
情報隠蔽・データ隠蔽との違い
この三語は混同されがちですが、抽象度が違います。情報隠蔽は「モジュールの内部の詳細を隠し、公開するインターフェースだけを見せる」という設計方針を指す上位概念です。データ隠蔽は、そのうち特にフィールドをprivateにして直接アクセスを禁じる実装レベルの手段を指します。カプセル化は、データと操作を束ねたうえで情報隠蔽・データ隠蔽を実現する仕組みそのものを指す言葉です。つまり「方針=情報隠蔽 → 実装手段=データ隠蔽 → それらをまとめた形=カプセル化」と押さえると混乱しません。
カプセル化のメリットと目的
カプセル化の目的は、内部を隠すこと自体ではなく、隠すことで得られる次の効果にあります。
- 想定外の変更を防ぐ:フィールドを直接書き換えられないため、値の範囲チェックをsetterに集約でき、不正な状態のオブジェクトが生まれにくい。
- 変更の影響範囲を局所化する:内部実装を差し替えても、公開メソッドのシグネチャが同じなら利用側は修正不要。改修コストが下がる。
- 使い方が明確になる:公開メソッドがそのクラスの「できること一覧」になり、チーム開発で誤った使い方を減らせる。
逆にすべてをpublicにすると、どこからでもフィールドを書き換えられ、仕様変更時に影響が全体へ波及します。カプセル化は「変更に強い設計」のための投資と捉えると理解しやすい。
Javaでのカプセル化の実装|privateフィールドとgetter/setter
基本形:フィールドをprivate、操作をpublicで公開する
もっとも基本的なパターンは、フィールドをprivateで隠し、読み書きが必要なものだけをgetter/setterで公開する形です。
public class BankAccount {
private long balance; // 外部から直接触れない
public long getBalance() {
return balance;
}
public void deposit(long amount) {
if (amount <= 0) {
throw new IllegalArgumentException("入金額は正の値");
}
balance += amount;
}
}
残高をpublic long balance;にしていれば、外部からaccount.balance = -1000;のような不正な代入が通ってしまいます。private+depositメソッド経由に限定することで、常にチェックを通した操作だけを許せます。
setterのバリデーションと読み取り専用getter
カプセル化の価値は、setterに検証ロジックを持たせて初めて発揮されます。単に代入するだけのsetterなら、publicフィールドと変わりません。
public void setAge(int age) {
if (age < 0 || age > 150) {
throw new IllegalArgumentException("年齢が不正");
}
this.age = age;
}
外部に変更させたくない値はsetterを作らずgetterだけ公開します。こうすれば読み取り専用プロパティとして扱え、生成後は不変(イミュータブル)に保てます。
Java 16以降のrecordで不変オブジェクトを簡潔に書く
「不変で、値を読むだけ」のデータ運搬用クラスなら、Java 16で正式化されたrecordが有効です。フィールドは自動でprivate finalになり、アクセサ・コンストラクタ・equalsが生成されるため、getter/setterを手書きせずにデータ隠蔽と不変性を両立できます。
public record Point(int x, int y) {}
// Point p = new Point(1, 2); p.x() で読み取り。setterは無く不変。
状態を持ち回すだけの値オブジェクトはrecord、内部状態を管理する振る舞いのあるクラスはprivateフィールド+メソッド、と使い分けると設計がすっきりします。
IDE自動生成のgetter/setterに頼りすぎない
IDEは全フィールドのgetter/setterを一括生成できますが、これを何も考えず使うと「全項目が外から自由に書き換え可能」なクラスになり、カプセル化の意味が失われます。生成後は「本当に外部から変更させる必要があるフィールドか」を1つずつ見直し、不要なsetterは削るのが実務の基本です。
アクセス修飾子と可視性|public・protected・default・private
Javaのアクセス修飾子は、メンバーをどこまで見せるかを4段階で制御します。カプセル化はこの可視性の使い分けで実現します。
| 修飾子 | 同一クラス | 同一パッケージ | サブクラス(別パッケージ) | それ以外 |
|---|---|---|---|---|
| private | ○ | × | × | × |
| (無指定=default) | ○ | ○ | × | × |
| protected | ○ | ○ | ○ | × |
| public | ○ | ○ | ○ | ○ |
基本方針は「フィールドはprivate、外部に見せる操作だけpublic」です。protectedは継承先に内部を委ねる修飾子で便利ですが、サブクラスが親の内部に依存するためカプセル化は弱まります。継承のためだけに安易にprotectedを広げず、必要最小限にとどめるのが定石です。
カプセル化が破綻する典型パターン
「フィールドをprivateにしてgetter/setterを付けた」だけでは、カプセル化できているとは限りません。現場で頻出する破綻パターンを挙げます。
- 全フィールドにgetter/setterを機械生成する:外から全項目を自由に書き換えられ、実質
publicフィールドと同じ。振る舞いを持たず値を保持するだけの「貧血ドメインモデル」に陥りやすい。setterは本当に必要なものだけにする。 - 可変オブジェクトの参照をそのまま返す:getterで内部の
Listや配列をそのまま返すと、呼び出し側がその中身を書き換えて内部状態を壊せる。コピーを返すかCollections.unmodifiableListで防ぐ。 - 内部表現をそのままAPIに晒す:内部で使うデータ構造をgetterで直接公開すると、後で実装を変えたときにインターフェースまで変わり、利用側が壊れる。公開するのは「意味のある値」に限る。
カプセル化の合否は「フィールドがprivateか」ではなく「外部が内部の不変条件を壊せないか」で判断します。
継承・ポリモーフィズムとの関係|三大要素の中での役割
カプセル化は単独で完結せず、他の二要素と噛み合って効果を発揮します。継承では、親クラスが内部をしっかり隠していれば、子クラスは公開された振る舞いだけに依存でき、親の実装変更に強くなります。ポリモーフィズムでは、各クラスが内部を隠して同じインターフェースを実装するからこそ、呼び出し側は具象を意識せず差し替えられます。三要素の詳細は、継承とは?オブジェクト指向での使いどころ・委譲との使い分け・アンチパターンとポリモフィズム(多態性)とは?読み方・種類・Javaの実装をわかりやすく解説で個別に解説しています。また、内部を隠して拡張に開くという発想は、【SOLID原則】オープンクローズドの原則とは何か?その概要と意義を解説にもつながります。
基本情報技術者試験で問われるカプセル化
「オブジェクト指向におけるカプセル化を説明したものはどれか」という設問は、基本情報技術者試験の頻出パターンです。正解の選択肢は「データとそのデータに関する手続(メソッド)を一つにまとめ、オブジェクトの内部を外部から隠すこと」という趣旨の記述になります。試験では次の紛らわしい選択肢と区別できれば正答できます。
- 「上位クラスの性質を下位クラスが引き継ぐ」→ 継承の説明。
- 「同じメッセージに対しオブジェクトごとに異なる動作をする」→ ポリモーフィズムの説明。
- 「共通する性質を抽出して一般化する」→ 汎化(抽象化)の説明。
キーワードは「データと手続をまとめる」「内部を隠す」の2点です。この2語が入っている選択肢がカプセル化だと覚えておくと確実です。
よくある質問
カプセル化を一言で言うと何ですか?
「データと、それを操作する処理を一つにまとめ、外部から直接触れないように隠すこと」です。使う側は内部を知らなくても、公開された操作だけで目的を達成できます。
情報隠蔽・データ隠蔽・カプセル化の違いは?
情報隠蔽は「内部詳細を隠して必要なものだけ公開する」という設計方針、データ隠蔽はフィールドをprivateにして直接アクセスを防ぐ実装手段、カプセル化はそれらをまとめて実現する仕組み全体を指します。
クラス(クラス化)とカプセル化は何が違う?
クラスは「データと操作をまとめる器」そのものを指します。カプセル化は、そのクラスの内部をprivateなどで隠し、公開する範囲を絞る設計方針です。クラスにまとめただけでは全項目が外から見える状態もあり得るため、まとめる(クラス化)と隠す(カプセル化)は別の段階と考えると整理できます。
getterとsetterは必ず両方作るべきですか?
いいえ。外部から変更させたくない値はsetterを作らず、getterだけの読み取り専用にします。全フィールドに両方を付けるのはカプセル化の目的に反するため、必要なものだけに絞ります。
C++やPythonでもカプセル化はできますか?
できます。C++はprivate/protected/publicで可視性を制御します。Pythonは言語仕様上の強制はなく、慣習として先頭にアンダースコアを付けた名前(_value)で「内部用」を示し、名前修飾(__value)で外部からの参照を難しくします。考え方はどの言語でも同じで、内部を隠して公開範囲を絞ります。
「オブジェクト指向におけるカプセル化を説明したものはどれか」の答えは?
「データとそのデータを操作する手続を一つにまとめ、オブジェクトの内部を外部から隠蔽すること」を述べた選択肢が正解です。「継承(性質の引き継ぎ)」「ポリモーフィズム(同一操作で異なる振る舞い)」との混同に注意します。