カプセル化とは?
オブジェクト指向プログラミングにおけるカプセル化とは、オブジェクトのデータとそのデータを扱うメソッドを、ふつうはクラスという1つの単位にまとめ、内部の詳細を隠して、ほかのコードが公開されたメソッドを通してしかオブジェクトを使えないようにすることです。
更新日: 2026年9月24日
銀行アプリが、各口座の残高をただの変数に保存しているとします。プログラムのどこにあるどの行でも account.balance = -500 と書けてしまい、それを止めるものは何もありません。本番環境でマイナスの残高が現れたら、コードベース全体から、それに触れたすべての場所を探し出さなければなりません。カプセル化はこれを解決します。残高はいくつかのメソッドを通してしか変更できず、それらのメソッドが、たとえば引き出し額が残高を超えてはいけないといったルールを守らせるのです。
カプセル化の仕組み
カプセル化は2つの考え方を組み合わせたものです。
- まとめること。 データ(残高)と、それを扱うコード(入金、引き出し)が同じクラスの中にあります。
- 隠すこと。 ほかのコードから見えるのは、クラスの公開インタフェースだけです。その背後にあるデータは内部のものなので、クラスが許す方法でしか変更できません。
クラスが守るルールを不変条件(インバリアント)と呼びます。ここでは「残高は決してマイナスにならない」です。すべての変更がクラスのメソッドを通るので、プログラムの残りの部分が何をしようと、不変条件は保たれます。
70
refused: not enough money
外のコードは acct.balance を読めますが、このプロパティにはセッターがないので、acct.balance = 1000 と書くと AttributeError が発生します。残高がおかしくなったとしても、調べるべきメソッドはちょうど2つだけです。
Pythonのカプセル化
Pythonには private キーワードがありません。代わりに、Pythonのプログラマなら誰でも知っている命名規則に頼っています。
name: 公開。インタフェースの一部です。_name: 内部用。読むことを止めるものは何もありませんが、先頭のアンダースコアは「これに依存しないで」という意味です。__name: Pythonがクラスの中で名前を変えます(名前修飾、ネームマングリング)。そのため、サブクラスが誤って上書きすることがありません。@property: メソッドを属性のように読めるものに変えます。そのため、呼び出し側のコードを変えずに、後からチェックを追加できます。
0
False
42
名前修飾が防ぐのは事故であって、アクセスではありません。修飾された名前を使えば今でもアクセスできます。Pythonは、プログラマがアンダースコアを尊重すると信頼しているのです。実際には、ほとんどのPythonのコードはアンダースコア1つと @property を使い、アンダースコア2つはサブクラス化を前提としたクラスのために取っておきます。クラスの書き方について詳しくは、Pythonのクラスのドキュメントを参照してください。
Java、C++、JavaScriptのカプセル化
ほかの言語では、隠すことが強制されます。Javaでは、private なフィールドはそのクラスの中でしか使えません。
public class BankAccount {
private int balance; // hidden from every other class
public int getBalance() { return balance; }
public void withdraw(int amount) {
if (amount > balance) throw new IllegalArgumentException("not enough money");
balance -= amount;
}
}
別のクラスで account.balance = 1000; と書くとコンパイルできず、javac は balance has private access in BankAccount と報告します。
| 言語 | 隠されたメンバ | 公開メンバ | 強制するもの |
|---|---|---|---|
| Python | _name、__name | name | 慣習 |
| Java | private int name | public int name | コンパイラ |
| C++ | private: のセクション | public: のセクション | コンパイラ |
| JavaScript | #name | name | JavaScriptエンジン |
C++では、class のメンバはデフォルトで非公開、struct のメンバはデフォルトで公開です。ES2022で追加されたJavaScriptの # によるプライベートフィールドは、クラスの外からはまったく読めません。それぞれについてはドキュメントで説明しています。Javaのアクセス修飾子、C++のアクセス指定子、JavaScriptのプライベートフィールドです。
カプセル化が大切な理由
- ルールが1か所にまとまります。 「残高を超える引き出しはできない」というチェックは、プログラム中のすべての引き出しの前ではなく、
withdrawの中に一度だけ書かれます。 - 内部を自由に変更できます。 お金を浮動小数点数ではなく整数のセント単位で保存することにしても、変わるのはクラスだけです。
deposit(100)を呼び出すコードはそのまま動き続けます。 - デバッグが楽になります。 値がおかしいとき、疑わしいのはそれを変更できる少数のメソッドだけで、プログラムのすべての行ではありません。
- 学ぶこともテストすることも少なくなります。 公開メソッドが3つのクラスは、その3つのメソッドを通して理解し、テストできます。
カプセル化はクラスより上のレベルでも働きます。2つの関数を公開し、10個の補助関数を内部にとどめているモジュールも同じようにカプセル化されていますし、プログラムにシステムコールを通してしかファイルを開かせないオペレーティングシステムもそうです。
カプセル化は抽象化とよく混同されます。抽象化はインタフェースが何を提供するかを決め、カプセル化はその背後にあるデータを守ります。抽象化のページで、2つを並べて詳しく比較しています。この2つにポリモーフィズムと継承を加えたものが、オブジェクト指向プログラミングの4つの柱です。
よくある間違い
- すべてのフィールドにゲッターとセッターを用意する。 どんな数でも受け付ける
setBalanceは何も守りません。各フィールドへの生のアクセスではなく、オブジェクトに必要な操作(withdraw)を用意してください。 - 内部のオブジェクトを返す。
self._itemsを返すメソッドは、呼び出し側に本物のリストを渡してしまい、呼び出し側はそれを変更できるようになります。代わりにコピーかタプルを返してください。 - privateをセキュリティとみなす。 アクセス修飾子が防ぐのはミスであって、攻撃ではありません。Javaのコードはリフレクションで非公開のフィールドを読めますし、Pythonのコードは
_nameを直接読めます。パスワードや鍵には、ハッシュ化や暗号化のような本当の保護が必要です。
次に読むページ
次は抽象化を読んでください。2つの考え方の違いを説明しています。その後でポリモーフィズムに進みましょう。Pythonコースではクラスを一歩ずつ作っていき、Javaコースではコンパイラによって強制されるカプセル化を学べます。
よくある質問
カプセル化を簡単に言うと何ですか?
deposit と withdraw を用意し、それらが先にルールをチェックします。名前はカプセル、つまり中身を内側に保つ殻に由来します。カプセル化の具体例は?
deposit と withdraw を通してしか変更できない銀行口座のクラスが、典型的な例です。Pythonの list もその1つで、append を呼ぶだけで、要素を保存するメモリを自分で管理することはありません。いくつかの関数だけを公開し、補助的な関数を非公開にしておくモジュールは、もっと大きな規模でのカプセル化です。カプセル化の3つの種類とは?
C++のカプセル化とは?どのように使いますか?
private を付け、それを読み書きする public なメンバ関数を用意します。class のメンバはデフォルトで非公開で、struct のメンバはデフォルトで公開です。クラスの外にあるコードが非公開のメンバを使おうとすると、コンパイラがそれを拒否します。