Java入門21 アクセス修飾子 publicとprivateの違い

Java入門の第21回です。前回からの続きですが、今回だけでも分かる内容になっています。このシリーズの第1回はこちらです。

今回は、アクセス修飾子について解説します。Javaのコードを書いていると必ず登場する「public」は、アクセス修飾子のひとつです。アクセス修飾子について理解を深めましょう。

アクセス修飾子とは

アクセス修飾子とは、クラスや変数、メソッドなどに対するアクセスを制御するためにつけるものです。Javaで利用できるアクセス修飾子には、以下の4種類があります。

  1. public …… プログラムのどこからでもアクセスできる
  2. protected …… 同一パッケージ、他のパッケージのサブクラスからのみアクセスできる
  3. なし(初期値) …… 同一パッケージからのみアクセスできる
  4. private …… 同一クラスからのみアクセスできる

上記4つのアクセス修飾子は、アクセス制限のゆるい順に並んでいます。アクセス制限が最もゆるいのが public で、どのクラスからでもアクセスできます。アクセス制限が最も厳しいのが private で、同一クラスからのみアクセスできます。

上記のうち、まずは最もゆるい public と、最も厳しい private の違いを理解しましょう。protected と なし(初期値) は、ある程度Javaを使いこなせるようなってから必要に応じて覚えていけばよいでしょう。

publicを付けると、どのクラスからでもアクセスできる

アクセス修飾子の public を付けると、どのクラスからでもアクセスできます。先頭にpublicキーワードを付けて宣言されているクラス、メソッド、変数などは、プログラムのどこからでもアクセスできます。

例えば、「public クラス」と書けば、そのクラスにはプログラムのどこからでもアクセスできます。「public メソッド」と書けば、そのメソッドにはプログラムのどこからでもアクセスできます。「public 変数」と書けば、その変数にはプログラムのどこからでもアクセスできます。

publicキーワードは、これまでサンプルコードのなかでたびたび登場しています。簡単なサンプルコードでは、特にアクセス制限を意識する必要がありませんでした。アクセス制限が必要ない場合には、publicを指定することが多くなります。

privateを付けると、同一クラスからのみアクセスできる

アクセス修飾子の private を付けると、同一クラスからのみアクセスできます。 先頭に private キーワードを付けて宣言されているクラス、メソッド、変数などは、それらが宣言されたクラス内からのみアクセスできます。

privateキーワードを使った実例を確認してみましょう。例えば、顧客の年齢や血液型などはあまり変化するものではないので、これらの変数の値はプログラムのどこからでも変更できてしまうよりは、ある程度アクセスしづらくしておく方が良いかもしれません。

class customer {
    //データをカプセル化
    private int age;    //年齢
    private String blood;    //血液型

    //データ参照用のメソッド
    public void printAge() {System.out.println("年齢は" + this.age + "歳です");}
    public void printBlood() {System.out.println("血液型は" + this.blood + "型です");}
}

上記コードでは、年齢を代入する変数ageと血液型を代入する変数bloodを宣言する際に、先頭にprivateを付けています。先頭にprivateをつけることで、他のクラスからはアクセスできなくなります。このように他のクラスからアクセスできなくすることを「不可視にする」あるいは「カプセル化する」といいます。

カプセル化したデータには、アクセッサ経由でアクセスする

以下のコードでは、privateを付けて年齢のデータを宣言しています。

class Customer {
    //データをカプセル化
    private int age;        //年齢

    //データ参照用のメソッド(アクセッサ)
    public void printCustomerAge() {
    	System.out.println("年齢は" + this.age + "歳です");
    }

    //データ更新用のメソッド(アクセッサ)
    public void setCustomerAge(int a) {
    	this.age = a;
    }
}

public class Test25 {
    public static void main(String[] args) {
        //インスタンスを生成
        Customer c1 = new Customer();
        //アクセッサ経由で年齢を更新
        c1.setCustomerAge(20);
        //アクセッサ経由で年齢を参照
        c1.printCustomerAge();
    }
}

上記コードの実行結果は、以下の通りです。

年齢は20歳です

上記コードでは、privateを付けて変数ageを宣言しているため、他のクラスからは変数ageにアクセスできません。private修飾子によってデータへのアクセスを制限することで、不用意なデータ更新が起きにくいプログラムにできます。

アクセス修飾子のなかで最もアクセス制限の厳しいprivateキーワードですが、同一クラスからならアクセスできます。上記コード例の場合には、同一クラス内に用意した setCustomerAge()メソッドやprintCustomerAge()メソッドからは、変数ageにアクセスできます。

他のクラスから年齢を確認したい場合には、変数ageに直接アクセスするのではなく、setCustomerAge()メソッドやprintCustomerAge()メソッドを経由することで年齢のデータ内容を更新したり確認することができます。

上記コードにおける setCustomerAge()メソッドやprintCustomerAge()メソッドのように、データにアクセスするためのメソッドを「アクセッサ」と呼びます。カプセル化したデータには、アクセッサ経由でアクセスします。

データをカプセル化するメリット

わざわざデータをカプセル化して、アクセッサ経由でないと更新や参照ができないようにするメリットはどこにあるのでしょうか。上記コードの例で考えてみましょう。

上記コードの例では、年齢を表す変数ageをカプセル化しています。もし変数ageに値を直接代入できるプログラムの場合、もしかするとage = 500;などのありえない数値を代入されてしまうかもしれません。人間の年齢が500歳ということはまずありえません。

あるいは、上記コードは顧客データを扱うプログラムですから、顧客年齢を20歳以上に制限したい場合もあるかもしれません。このような場合にも変数ageの値を直接更新させずに必ずアクセッサを経由させることで、アクセッサの時点で入力内容にチェックを掛けることが可能になります。

入力内容チェックの一例を挙げるなら、例えば以下のようなコードにすることでありえない年齢の入力を未然に防げるかもしれません。

class Customer {
    //データをカプセル化
    private int age;        //年齢

    //データ参照用のメソッド(アクセッサ)
    public void printCustomerAge() {
    	System.out.println("年齢は" + this.age + "歳です");
    }

    //データ更新用のメソッド(アクセッサ)
    public void setCustomerAge(int a) {
    	if(a < 20) {
    	    System.out.println("【エラー】年齢が20歳未満です");
    	    System.exit(0);  //プログラムをここで終了
    	} else if(a >= 120) {
    	    System.out.println("【エラー】年齢が120歳以上です");
    	    System.exit(0);  //プログラムをここで終了
    	} else {
    	    this.age = a;
    	}
    }
}

public class Test25 {
    public static void main(String[] args) {
        //インスタンスを生成
        Customer c1 = new Customer();
        //アクセッサ経由で年齢を更新
        c1.setCustomerAge(19);
        //アクセッサ経由で年齢を参照
        c1.printCustomerAge();
    }
}

上記コードの実行結果は、以下の通りです。

【エラー】年齢が20歳未満です

上記コードでは、変数ageをカプセル化してアクセッサ経由でしか更新できないようにしています。そして、変数ageへのアクセッサとなるsetCustomerAge()メソッドでは、ありえない値に更新されてしまうことがないように、更新を指定された値に対してif文でチェックをしています。

更新を指定された値が19なので、if(a < 20)という条件に合致しているため、エラーメッセージを表示して、そこでプログラムを終了しています。その後の処理は実行されないので、変数ageの値が19に更新されてしまうことはありません。

次回へ続きます。