CodeGym /コース /JAVA 25 SELF /アクセス修飾子と変数のスコープ

アクセス修飾子と変数のスコープ

JAVA 25 SELF
レベル 8, レッスン 5
使用可能

1. はじめに

Java でも現実と同じく、すべてが常に誰にでも公開されてよいわけではありません。家に例えると、隣人に寝室まで自由に出入りしてほしくはありませんよね? プログラムでも同様に、外部から触れてほしくない変数やメソッドには、外からアクセスできないように「ドアを閉める」ことがあります。

そのため Java には アクセス修飾子 という、変数やメソッドをどこから使えるかを指定する特別なキーワードがあります。たとえば public や private です。

主なアクセス修飾子

修飾子 可視範囲
public
クラスが見える場所ならどこでも(他のパッケージや別ファイルからも)
private
同じクラスの内部のみ
(修飾子なし)
同一パッケージ内のみ(つまり同じディレクトリのクラス内)

他にも修飾子はありますが、それらは継承に進んだときに扱います。

実例

public class User 
{
    public String name;           // 誰からでも見える
    private int age;              // User クラスの内部でのみ見える

    public void sayHello() 
    {
        System.out.println("こんにちは、私の名前は " + name);
    }

    private void secretMethod() 
    {
        System.out.println("これは秘密のメソッドです!");
    }
}

説明:

  • name — User クラスが使える場所ならどこでも見える。
  • age — User クラスの内部でのみ見える。
  • sayHello() — public メソッドで、他のどのクラスからでも呼び出せる。
  • secretMethod() — private メソッドで、外部からは呼び出せない。

実際の課題ではどう使う?
たとえば銀行口座を表すクラスがあるとします。残高を誰でも直接変更できるようにはしたくありませんよね。そのため、残高の変数は private にし、それを操作するための専用メソッドを用意します。

なぜすべてを public にしてはいけないのか?

「じゃあ全部を public にして、悩まずに済ませよう!」という誘惑があるかもしれません。しかしそれはカオスへの道です。誰もがあなたの残高やユーザー名を変更できたり、本来は内部専用のメソッドを呼べてしまったりします。大規模なプログラムでは、これがエラーや不可解なバグの原因になります。

Java 開発者の黄金律:
まずはすべてを private にし、本当に必要なものだけを外に公開する。

2. 変数のスコープ

スコープとは、変数が「存在し」、使用できるコードの範囲のことです。この範囲を外れると、その変数は「消え」て、存在しなかったかのようになります。

Java にはスコープによっていくつかの種類の変数があります:

  • ローカル変数 — メソッドや { ... } のブロック内で宣言される。
  • メソッドのパラメータ — メソッドの丸括弧内で宣言される。
  • クラスのフィールド — クラス内で宣言されるが、メソッドの外側にある。

変数が { ブロック } の内側で宣言されている場合、
その ブロック の内側でのみ可視で、
外側からはアクセスできません。
ローカル変数のスコープ

ローカル変数

ローカル変数は、宣言されたメソッドまたはブロックの内部でのみ生きています。

void printSum(int a, int b) 
{
    int sum = a + b; // ローカル変数
    System.out.println(sum);
}
// ここでは sum はもう存在しない!

メソッドの外で sum にアクセスしようとすると、コンパイルエラー「変数 sum が見つかりません」になります。

メソッドのパラメータ

パラメータも変数ですが、メソッド内部でのみ生存します。

void greet(String name) 
{
    System.out.println("こんにちは、" + name);
}
// ここでは name はもう存在しない!

クラスのフィールド(メンバー変数)

クラスのフィールドは、クラス内で(メソッドの外で)宣言されます。これらはこのクラスのすべてのメソッドから見えます。

public class Counter 
{
    private int count = 0; // クラスのフィールド

    public void increment() 
    {
        count++; // フィールドを使える
    }

    public int getCount() 
    {
        return count; // こちらでもフィールドを使える
    }
}

3. 変数のシャドーイング(Shadowing)

シャドーイング(shadowing)とは、あるスコープで、外側のスコープと同じ名前の変数(またはパラメータ)を宣言してしまう状況のことです。そのブロック内では「新しい」名前が古いものを隠してしまうため、外側の値には直接アクセスできなくなります。

シャドーイングの例:

class ShadowDemo 
{
    int value = 10; // クラスのフィールド

    void printValue() 
    {
        System.out.println(value);  // 10 — クラスのフィールドを出力
        int value = 5; // ローカル変数がクラスのフィールドをシャドーイング
        System.out.println(value); // 10 ではなく 5 を出力
    }
}

この例では、int value = 5; と書いたとき、同名のクラスフィールドよりも優先される新しいローカル変数を宣言しています。メソッド printValue() の中で value にアクセスすると、クラスフィールドではなくローカル変数が使われます。

それでもなお クラスの静的フィールド にアクセスしたい場合は、プレフィックスとしてクラス名を使います:

class ShadowDemo 
{
    static int value = 10; // クラスの静的フィールド 

    void printValue() 
    {
        System.out.println(value);      // 10 — クラスのフィールド
        int value = 5;
        System.out.println(value);      // 5 — ローカル変数
        System.out.println(ShadowDemo.value); // 10 — クラスの静的フィールド('ShadowDemo' 経由でアクセス)
    }
}

非静的なクラスフィールド にアクセスしたい場合は、キーワード this を使います。これは現在のオブジェクトのインスタンスを指します。

class ShadowDemo 
{
    int value = 10;

    void printValue() 
    {
        System.out.println(value);      // 10 — クラスのフィールド
        int value = 5;
        System.out.println(value);      // 5 — ローカル変数
        System.out.println(this.value); // 10 — クラスのフィールド('this' 経由でアクセス)
    }
}

4. 実践: アクセス修飾子とスコープ

学習用の簡単な学生管理アプリを発展させていきましょう。フィールドやメソッドに異なるアクセス修飾子を付けてみます。

例: Student クラス

public class Student 
{
    public String name;        // 学生名(誰からでも見える)
    private int age;           // 年齢(クラス内部でのみ見える)

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

    public void sayHello() 
    {
        System.out.println("こんにちは、私の名前は " + name);
    }

    private void printSecret() 
    {
        System.out.println("私の年齢: " + age);
    }

    public void revealSecret() 
    {
        printSecret(); // クラス内からは private メソッドを呼び出せる
    }
}

Student クラスの使用例

public class Main 
{
    public static void main(String[] args) 
    {
        Student s = new Student("Vasya", 20);
        s.sayHello();       // OK: public メソッド
        s.revealSecret();   // OK: public メソッド(内部で private を呼び出す)

        s.age = 30;      // エラー! フィールド age は private

        s.printSecret(); // エラー! メソッド printSecret は private
    }
}

5. アクセス修飾子とスコープで陥りがちなミス

間違い1: 何もかもを public に — それはカオスへの近道!
すべてのフィールドやメソッドを公開にすると、誰がどのようにデータを変更しているのかを制御できなくなります。フィールドは private にし、本当に必要なものだけを公開しましょう。

間違い2: スコープ外で変数を使おうとしてしまう。
たとえば、メソッド内で変数を宣言したのに、その外側からアクセスしようとするとコンパイルエラーになります。ローカル変数は自分のブロック { ... } の中だけで生きています。

間違い3: 名前の衝突(シャドーイング)。
メソッド内でクラスフィールドと同じ名前の変数を宣言すると、どの変数を使っているのか混乱しがちです。this を使ってクラスフィールドを明示しましょう: this.value。

間違い4: 別のクラスから private メソッドやフィールドにアクセスしようとする。
メソッドやフィールドが private の場合、それらは同じクラスの内部からのみアクセス可能です。外部からアクセスしようとするとコンパイルエラーになります。

間違い5: ループやブロックのスコープを忘れる。
ループや任意のブロック { } の中で宣言された変数は、そのブロックの中でのみ生きています。ブロックの外では存在しません。

1
タスク
JAVA 25 SELF, レベル 8, レッスン 5
ロック未解除
村の住民の公開名簿 🏡
村の住民の公開名簿 🏡
1
タスク
JAVA 25 SELF, レベル 8, レッスン 5
ロック未解除
極秘の顧客銀行口座 🔒
極秘の顧客銀行口座 🔒
1
タスク
JAVA 25 SELF, レベル 8, レッスン 5
ロック未解除
製造用ボックスのサイズ設定 📦
製造用ボックスのサイズ設定 📦
1
タスク
JAVA 25 SELF, レベル 8, レッスン 5
ロック未解除
学校の生徒情報のデータベース管理 👩‍🎓
学校の生徒情報のデータベース管理 👩‍🎓
1
アンケート/クイズ
メソッド、レベル 8、レッスン 5
使用不可
メソッド
メソッドの宣言と呼び出し
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION