【JAVA】レッスン5-03:カプセル化を理解しよう

記事内に商品プロモーションを含む場合があります

この記事で学べる知識:カプセル化

この記事の練習問題で使用する知識:
基礎構文、制御構造、メソッド、コレクション(レッスン1~4)クラスの定義と使用コンストラクタカプセル化クラスメンバとインスタンスメンバメソッドのオーバーライドクラスの継承抽象クラスインターフェース

<<前のページ 問題集Top 次のページ>>

JAVAの文法「カプセル化」とは

ここではカプセル化の意味や使い方を学習します。必要ない方はここをクリックして練習問題へ飛びましょう。



プログラミングではクラスを使用してデータを安全に管理するために「カプセル化」を利用します。

カプセル化によりデータが不正に操作されることを防ぎ、クラス内部のデータを安全に保つことが可能です。

カプセル化とは?

カプセル化(Encapsulation)はクラス内のデータ(フィールド)や処理を他のクラスから隠し、アクセス制御を行うことを言います。

これにより外部のコードが直接データに触れることができなくなり、不正なデータ操作や意図しないエラーを防ぎます。

Javaではデータを保護するために、アクセス修飾子を使用して変数やメソッドの可視性を制御します。

例えばprivate修飾子を使うと、その変数は同じクラス内からのみアクセス可能になり、外部からは直接変更できません。

このようにしてクラス内部のデータが隠蔽され、データの保護が実現します。

アクセス修飾子の役割

Javaではアクセス修飾子を用いてクラスやそのメンバ(変数、メソッドなど)へのアクセス範囲を設定できます。代表的な修飾子として以下が挙げられます。

  • private:同じクラス内でのみアクセス可能
  • protected:同じパッケージ内やサブクラスからアクセス可能
  • public:どこからでもアクセス可能
public class Test {
    // テストクラスのフィールド
    private int a; //変数aはこのTestクラスの中からのみアクセス可能。
    protected int b; // 変数bは同じパッケージ内やサブクラスからアクセス可能。
    public String c; //変数cはどこからでもアクセス可能。
}

カプセル化は通常はprivateで変数宣言され、外部には公開しません。

その代わりに、クラス外部からデータを取得・更新するためのメソッド(ゲッター、セッター)をpublicとして提供します。

ゲッターとセッター

カプセル化されたデータにアクセスするために、Javaでは「ゲッター」と「セッター」というメソッドを使用します。

ゲッターは変数の値を取得するためのメソッドで、セッターは変数の値を設定するためのメソッドです。

以下にprivateで定義されたscore変数に対するゲッターとセッターを例として示します。

public class Player {
    // カプセル化されたフィールド
    private int score;

    // ゲッターメソッド(scoreの値を取得)
    public int getScore() {
        return score;
    }

    // セッターメソッド(scoreの値を設定)
    public void setScore(int score) {
        // scoreが0以上の場合のみ設定
        if (score >= 0) {
            this.score = score;
        }
    }
}

この例ではscoreフィールドはprivateとして宣言され、直接アクセスできないようにしています。

getScore()メソッドでscoreの値を取得でき、setScore(int score)メソッドで値を更新できます。

またsetScoreではscoreの値が0以上の場合のみ更新されるため、データの整合性も保たれます。

カプセル化された変数に直接アクセスするエラー例

カプセル化ではフィールドがprivateで定義されるため、クラス外から直接アクセスするとエラーが発生します。

以下はPlayerクラスのscoreフィールドにゲッターを使わずにアクセスしようとする例です。

public class Player {
    private int score;

    public Player(int score) {
        this.score = score;
    }
}

public class Main {
    public static void main(String[] args) {
        Player player = new Player(100);
        
        // ゲッターを使わずにscoreに直接アクセスしようとする
        System.out.println(player.score); // コンパイルエラー
    }
}

このコードを実行すると、以下のようなコンパイルエラーメッセージが表示されます。

error: score has private access in Player
        System.out.println(player.score);
                                ^

このエラーはscoreprivateで宣言されているため、Playerクラスの外部から直接アクセスできないことが原因です。

解決するためにはPlayerクラス内にゲッター(getScore())メソッドを定義し、scoreにアクセスできるようにします。

public class Player {
    private int score;

    public Player(int score) {
        this.score = score;
    }

    // ゲッターメソッド
    public int getScore() {
        return score;
    }
}

public class Main {
    public static void main(String[] args) {
        Player player = new Player(100);
        
        // ゲッターを使用してscoreにアクセス
        System.out.println(player.getScore()); // 正常に出力される
    }
}

これによりMainクラスからもgetScore()メソッドを通じてscoreの値にアクセスできるようになり、エラーが解消されます。

まとめ

カプセル化はデータを保護し、クラスの一貫性を保つための重要な概念です。

アクセス修飾子とゲッター・セッターを組み合わせることで、外部からの不正な操作を防ぎ、オブジェクト指向プログラミングの基本である「データの安全性」を確保できます。

カプセル化を理解することで、より堅牢でメンテナンス性の高いコードを書くことができるでしょう。

JAVA練習問題4-2_カプセル化を使った学生情報管理プログラム

学生の情報を管理するプログラムを作成しましょう。

このプログラムでは、学生の名前と年齢を格納し、これらの情報をカプセル化を使って管理します。

また、複数の学生情報をリストに格納し、一覧表示する機能を実装します。

この問題の要件

以下の要件に従って作成して下さい。

  1. Student クラスを作成し、以下のフィールドを持つこと。
    • name(学生の名前): String
    • age(学生の年齢): int
  2. Student クラスには以下のメソッドを持つこと。
    • getName メソッド: 学生の名前を返す。
    • setName メソッド: 学生の名前を設定する。
    • getAge メソッド: 学生の年齢を返す。
    • setAge メソッド: 学生の年齢を設定する。
  3. メインプログラムで、複数の学生情報を格納するリストを作成し、学生情報を追加する機能を実装すること。
  4. 全ての学生情報を一覧表示する機能を実装すること。

ただし、以下のような実行結果となること。

----- ↓出力される結果の例↓ -----

学生の名前を入力してください(終了するには 'exit' と入力):
山田太郎
学生の年齢を入力してください:
20
学生の名前を入力してください(終了するには 'exit' と入力):
佐藤花子
学生の年齢を入力してください:
19
学生の名前を入力してください(終了するには 'exit' と入力):
exit
登録された学生の情報:
名前: 山田太郎, 年齢: 20
名前: 佐藤花子, 年齢: 19

この問題を解くヒント

1からコードを組み立てることが難しい場合は、以下のヒントを開いて参考にしましょう。

ヒント1【コードの構成を見る】

正解のコードは上から順に以下のような構成となっています。

1:ArrayListとScannerクラスをインポート
2:Studentクラスの定義
  □ Studentクラス内でnameとageフィールドをprivateで宣言
  □ Studentクラスのコンストラクタの定義(nameとageを初期化)
  □ getNameメソッドの定義(nameフィールドを取得)
  □ setNameメソッドの定義(nameフィールドを設定)
  □ getAgeメソッドの定義(ageフィールドを取得)
  □ setAgeメソッドの定義(ageフィールドを設定)
3:StudentManagementクラスの定義
  □ mainメソッドの定義
  □ □ Scannerオブジェクトscannerを初期化
  □ □ ArrayListオブジェクトstudentsを初期化
  □ □ whileループの開始
  □ □ □ 「学生の名前を入力してください(終了するには ‘exit’ と入力):」と出力
  □ □ □ ユーザー入力を受け取り、変数nameに代入
  □ □ □ if文でnameが「exit」と同じかを判定
  □ □ □ □ nameが「exit」と同じ場合、breakでループを終了
  □ □ □ 「学生の年齢を入力してください:」と出力
  □ □ □ ユーザーから年齢を入力し、変数ageに代入
  □ □ □ scanner.nextLine()で改行を消費
  □ □ □ Studentオブジェクトを生成し、studentsリストに追加
  □ 「登録された学生の情報:」と出力
  □ forループでstudentsリストの各Studentオブジェクトを取得
  □ □ StudentオブジェクトのgetNameとgetAgeメソッドを使用して情報を出力

ヒント2【穴埋め問題にする】

以下のコードをコピーし、コメントに従ってコードを完成させて下さい。

import java.util.ArrayList;
import java.util.Scanner;

// 学生情報を管理するクラス
class Student {
    /*【穴埋め問題1】
    ここにnameとageフィールドをprivateで宣言してください。
    */

    // コンストラクタ
    public Student(String name, int age) {
        /*【穴埋め問題2】
        ここでnameとageフィールドに引数の値を代入するコードを書いてください。
        */
    }

    // 名前を取得するメソッド
    /*【穴埋め問題3】
    ここにnameフィールドを取得するgetNameメソッドをpublicで定義してください。
    */

    // 名前を設定するメソッド
    /*【穴埋め問題4】
    ここにnameフィールドを設定するsetNameメソッドをpublicで定義してください。
    */

    // 年齢を取得するメソッド
    /*【穴埋め問題5】
    ここにageフィールドを取得するgetAgeメソッドをpublicで定義してください。
    */

    // 年齢を設定するメソッド
    /*【穴埋め問題6】
    ここにageフィールドを設定するsetAgeメソッドをpublicで定義してください。
    */
}

public class StudentManagement {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        ArrayList<Student> students = new ArrayList<>();

        // 学生情報を入力してリストに追加
        while (true) {
            System.out.println("学生の名前を入力してください(終了するには 'exit' と入力):");
            String name = scanner.nextLine();
            if (name.equalsIgnoreCase("exit")) {
                break;
            }

            System.out.println("学生の年齢を入力してください:");
            int age = scanner.nextInt();
            scanner.nextLine(); // 改行を消費

            // 学生オブジェクトを作成してリストに追加
            students.add(new Student(name, age));
        }

        // 学生情報を一覧表示
        System.out.println("登録された学生の情報:");
        for (Student student : students) {
            System.out.println("名前: " + student.getName() + ", 年齢: " + student.getAge());
        }
    }
}

この問題の穴埋めコードは以上です。

このヒントを見てもまだ回答を導き出すのが難しいと感じる場合は、先に正解のコードと解説を見て内容を理解するようにしましょう。

解答例解説

この問題の一つの正解例とそのコードの解説を以下に示します。

正解コードの例

例えば以下のようなプログラムが考えられます。

import java.util.ArrayList;
import java.util.Scanner;

// 学生情報を管理するクラス
class Student {
    private String name; // 学生の名前
    private int age;     // 学生の年齢

    // コンストラクタ
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 名前を取得するメソッド
    public String getName() {
        return name;
    }

    // 名前を設定するメソッド
    public void setName(String name) {
        this.name = name;
    }

    // 年齢を取得するメソッド
    public int getAge() {
        return age;
    }

    // 年齢を設定するメソッド
    public void setAge(int age) {
        this.age = age;
    }
}

public class StudentManagement {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        ArrayList<Student> students = new ArrayList<>();

        // 学生情報を入力してリストに追加
        while (true) {
            System.out.println("学生の名前を入力してください(終了するには 'exit' と入力):");
            String name = scanner.nextLine();
            if (name.equalsIgnoreCase("exit")) {
                break;
            }

            System.out.println("学生の年齢を入力してください:");
            int age = scanner.nextInt();
            scanner.nextLine(); // 改行を消費

            // 学生オブジェクトを作成してリストに追加
            students.add(new Student(name, age));
        }

        // 学生情報を一覧表示
        System.out.println("登録された学生の情報:");
        for (Student student : students) {
            System.out.println("名前: " + student.getName() + ", 年齢: " + student.getAge());
        }

    }
}

正解コードの解説

このコードの構造と、カプセル化の役割について詳しく理解していきましょう。

Student クラスの定義

class Student {
    private String name; // 学生の名前
    private int age;     // 学生の年齢
}

Student クラスは学生の情報を管理するクラスで、「カプセル化」を使って名前と年齢のデータを保護しています。

ここでprivate 修飾子を使うことで、このクラスの外部から nameage に直接アクセスできないようにしています。

これによりデータが意図しない方法で変更されるのを防ぐことができます。

コンストラクタの定義

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

コンストラクタは Student クラスのオブジェクトが生成されるときに呼び出され、渡された nameage をフィールドに設定します。

this.name のように this キーワードを使うことで、フィールドと引数を区別しています。

ゲッターとセッター

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public int getAge() {
    return age;
}

public void setAge(int age) {
    this.age = age;
}

ここで「カプセル化」の重要な役割がさらに明らかになります。

getNamesetAge といったメソッドは「ゲッター」「セッター」と呼ばれ、それぞれフィールドの値を取得・設定するためのメソッドです。

フィールドを private にすることで外部から直接アクセスできなくし、代わりに public なゲッターとセッターを通じて安全にアクセスできるようにしています。

この方法によりage フィールドの値を外部のコードから意図しない方法で変更されないようにすることが可能です。

StudentManagement クラスと main メソッドの開始

public class StudentManagement {
    public static void main(String[] args) {

StudentManagement クラスには main メソッドが含まれ、Javaプログラムのエントリーポイントとなっています。

このメソッドでプログラムの実行が始まり、ユーザーから学生の情報を入力し、管理する機能を提供します。

スキャナーとリストの初期化

Scanner scanner = new Scanner(System.in);
ArrayList<Student> students = new ArrayList<>();

Scanner クラスを使ってユーザー入力を取得し、ArrayList を使って複数の Student オブジェクトをリストに格納します。

学生情報の入力と追加

while (true) {
    System.out.println("学生の名前を入力してください(終了するには 'exit' と入力):");
    String name = scanner.nextLine();
    if (name.equalsIgnoreCase("exit")) {
        break;
    }

    System.out.println("学生の年齢を入力してください:");
    int age = scanner.nextInt();
    scanner.nextLine(); // 改行を消費

    students.add(new Student(name, age));
}

この部分ではユーザーに学生の名前と年齢を入力させ、入力された情報を基に新しい Student オブジェクトを作成して students リストに追加します。

while ループを使いexit と入力されるまで繰り返し情報を追加できます。

学生情報の表示

System.out.println("登録された学生の情報:");
for (Student student : students) {
    System.out.println("名前: " + student.getName() + ", 年齢: " + student.getAge());
}

最後にfor-each ループを使って students リスト内のすべての Student オブジェクトの名前と年齢を表示します。

ここで getName()getAge() メソッドを使用して Student オブジェクトのデータを取得し、安全に表示します。

まとめ

このコードではJavaの「カプセル化」の概念を使って Student クラスのデータを保護しています。

private フィールドと public なゲッター・セッターの組み合わせにより、クラス外からの不正なアクセスを防ぎ、コードの安全性が向上します。

カプセル化はJavaにおける重要な概念であり、これを理解することでより安全で保守性の高いプログラムが書けるようになります。

<<前のページ 問題集Top 次のページ>>

この問題への質問・コメント

この問題を作成するにあたりAIを活用しています。

問題ないことは確認していますが、もし間違いや表現の違和感などありましたら、ご指摘頂けると大変助かります。






    JAVAテキスト&問題集へ戻る
    トップページへ戻る