アットウィキロゴ
Javaプログラミング入門
掲示板 掲示板 ページ検索 ページ検索 メニュー メニュー

Javaプログラミング入門

16. カプセル化

最終更新:

javatutorial

- view
管理者のみ編集可

これまでのやり方の不便な点

前回までに、クラスを作ってデータをまとめ、コンストラクタを使って安全に初期化する方法を学びました。
しかし、これまでのプログラムにはまだ「データが壊されてしまう危険性」が残っています。

たとえば、生徒の点数を管理するStudentクラスで考えてみましょう。
Student s1 = new Student("山田", 80);
s1.score = -100; // ありえない点数を直接入れられてしまう!
変数(フィールド)に直接アクセスできる状態だと、上記のようにマイナスの点数や、1万点といった「テストの点数としてありえない異常な値」を外から勝手に入れられてしまいます。
誰かが間違えて異常な値を入れてしまうと、クラスのデータへの信頼がなくなり、計算結果がおかしくなるなどプログラム全体に悪影響を及ぼしてしまいます。

カプセル化とは?

このような不便や危険を解決するのが、オブジェクト指向の重要な考え方である「カプセル化」です。
カプセル化とは、一言でいうと「大切なデータを頑丈なカプセルの中に隠し、安全な方法でしか操作できないようにすること」です。

身近な例で「自動販売機」を想像してみてください。
私たちは自動販売機の中にある「お金を入れる金庫」や「ジュースの在庫」に直接手で触れることはできません。(データが隠されている状態)
その代わり、「お金を入れる口」や「ボタン」という決められた操作方法だけが用意されています。(安全な操作方法)
もし金庫や在庫に直接触れてしまったら、お金を払わずにジュースを出したり、お釣りを全部持っていかれたりしてしまいますよね。

プログラムも同じで、フィールド(データ)には直接触らせず、メソッド(決められた操作)を通しておこなうのが安全な設計の基本です。

アクセス修飾子によるデータの隠蔽

データをカプセルの中に隠すために、Javaには「アクセス修飾子」という機能が用意されています。
アクセス修飾子とは、クラスや変数、メソッドの先頭につけて「どこからアクセスできるか」を制限するキーワードです。

ここでは代表的な2つを覚えましょう。
アクセス修飾子 意味 例え
public どこからでも自由にアクセスできる 誰でも押せる自動販売機のボタン
private 自分自身のクラスの中からしかアクセスできない 自動販売機の中にある金庫

これを使って、Studentクラスのフィールドにprivateをつけてみます。

サンプル:Student.java
  1. public class Student {
  2. // フィールドをprivateにして外部から隠す
  3. private String name;
  4. private int score;
  5.  
  6. public Student(String name, int score) {
  7. this.name = name;
  8. this.score = score;
  9. }
  10. }
このようにprivateをつけると、別のクラス(Mainクラスなど)からは s1.score = -100; のような直接の操作ができなくなり、コンパイルエラーとして弾かれるようになります。
これでデータは守られました!

ゲッターとセッター(Getter / Setter)

しかし、フィールドをprivateにして完全に閉じ込めてしまうと、「点数を見ることも、正しい点数に変更することもできない」という困った状態になります。
そこで、外部とやり取りするための「窓口」となるメソッドを用意します。
これが「ゲッター(Getter)」と「セッター(Setter)」と呼ばれるメソッドです。

ゲッター: privateなフィールドの値を「取得(Get)」するためのメソッド。

セッター: privateなフィールドの値を「変更(Set)」するためのメソッド。


メソッドのアクセス修飾子はpublicにして、外部から呼び出せるようにします。

サンプル:Student.java
  1. public class Student {
  2. private String name;
  3. private int score;
  4.  
  5. public Student(String name, int score) {
  6. this.name = name;
  7. this.score = score;
  8. }
  9.  
  10. // nameのゲッター(値を見るだけ)
  11. public String getName() {
  12. return this.name;
  13. }
  14.  
  15. // scoreのゲッター
  16. public int getScore() {
  17. return this.score;
  18. }
  19.  
  20. // scoreのセッター(値を安全に変更する)
  21. public void setScore(int score) {
  22. // もし0点〜100点の正しい範囲なら、変更を許可する
  23. if (score >= 0 && score <= 100) {
  24. this.score = score;
  25. } else {
  26. System.out.println("エラー:点数は0から100の間で入力してください。");
  27. }
  28. }
  29. }

セッターの素晴らしいところは、メソッドの中にif文などを使って「不正な値かどうかのチェック」を入れられることです。
これにより、マイナスの点数などの異常な値が来たら弾くことができ、オブジェクトのデータが常に正しい状態に保たれます。

実際にMainクラスから使ってみましょう。

サンプル:Main.java
  1. public class Main {
  2. public static void main(String[] args) {
  3. Student s1 = new Student("山田", 80);
  4.  
  5. // 直接 s1.score にはアクセスできないため、ゲッターを使う
  6. System.out.println(s1.getName() + "さんの点数は" + s1.getScore() + "点です。");
  7.  
  8. // 正しい点数をセッターで設定する
  9. s1.setScore(95);
  10. System.out.println("変更後: " + s1.getScore() + "点");
  11.  
  12. // ありえない点数をセッターで設定しようとする
  13. s1.setScore(-100);
  14. System.out.println("異常な値を入れた後: " + s1.getScore() + "点");
  15. }
  16. }
実行結果
山田さんの点数は80点です。
変更後: 95点
エラー:点数は0から100の間で入力してください。
異常な値を入れた後: 95

異常な値 -100 を入れようとしたときはエラーメッセージが表示され、中のデータは 95 のまま守られていることがわかりますね!

💡注意!
「すべてのフィールドに対して、必ずゲッターとセッターの両方を作らなければならない」というのはよくある誤解です。
たとえば、上記のStudentクラスにはnameのゲッターはありますが、セッターはありません。
つまり「名前は途中で変更できない(読み取り専用の)データ」として設計していることになります。
必要な窓口(メソッド)だけを用意するのも、カプセル化の重要なテクニックです。

コラム

レコード(Record)という新しい仕組み

Java 14からは、「データを保持するだけのクラス」をもっと簡単に書けるrecord(レコード)という機能が追加されました。
フィールドをカプセル化して、コンストラクタやゲッターを書くという作業は毎回書くのが大変だったため、レコードを使うと数行で安全なデータの入れ物が作れるようになっています。
今はまだ基本のクラスの書き方をしっかり覚える時期ですが、将来もっと便利な書き方に出会えるのを楽しみに学習を進めてくださいね!

最近更新されたスレッド
人気記事ランキング
ウィキ募集バナー