第3章 アクセス修飾子とカプセル化
3.1 メンバのアクセスを制限する
前章では雛形であるクラスを用いて、オブジェクトを生成する方法を学習してきました。本節では、実際にアクセス制限を行う簡単なプログラムを作成し、「クラスメンバへのアクセスを制限する」方法ついて説明していきます。
3.1.1 メンバ変数に直接アクセスする方法
アクセス制限の方法を知る前に、今まで作成していたアクセス制限の無いプログラムを再確認します。
以下のフィールド変数に直接アクセスするプログラムを見て下さい。
① ソース・フォルダー: myproj_basic/src
② パッケージ: jp.co.f1.basic.ch03
③ 名前: DirectAccessObjectVariable
④ 作成するメソッド・スタブの選択: public static void main(String[] args) にチェックを入れる
package jp.co.f1.basic.ch03; class Computer1 { String os; int memory; public void show() { System.out.println("パソコンのOSは「" + os + "」です。"); System.out.println("メモリサイズは「" + memory + "MByte」です。"); } } public class DirectAccessObjectVariable { public static void main(String[] args) { Computer1 com = new Computer1(); com.os = "WindowsXP"; // フィールド変数os(メンバ変数)に直接アクセスし代入 com.memory = 2048; // フィールド変数memory(メンバ変数)に直接アクセスし代入 com.show(); } }
このプログラムではmainメソッドでフィールド変数に直接アクセスし、OS名やメモリサイズの値を代入しています。パソコンのOSを「WindowsXP」、メモリサイズを「2048」と設定(代入)しているわけですが、この処理では問題が起きてしまう場合があります。
DirectAccessObjectVariableのmainメソッドの仕組みでは、以下のような記述ができてしまいます。
public class DirectAccess1 { public static void main(String[] args) { Computer01 com = new Computer1(); com.os = "WindowsXP"; com.memory = -5445; com.show(); } }
抜粋したこの下記のコードは何を意味しているでしょうか。これまで学習してきた内容で考えると、変数comが指すオブジェクトのフィールド変数memoryの値を-5445と設定するという処理が行われています。しかし、この処理はおかしな状況です。本来パソコンはメモリのサイズをマイナス値にすることはありません。
com.memory = -5445;
本来ならば、このような不具合がおこならないように、様々な仕組みを組み込んでいきます。
ではその仕組みについて次の項より1つずつみていきましょう。
3.1.2 メンバにprivate修飾子をつける
前項のDirectAccessObjectVariableでメモリサイズにマイナスな値が入ってしまう誤りは何が原因だったでしょうか。その原因はメンバに制限なくアクセスし、勝手な値(ここでは-5445)を代入してしまったことにあるといえます。Javaプログラミングではこのような間違いをおこさせないように、クラスの外からメンバに自由にアクセスできない仕組みを設けることが可能です。そのようなクラスの外から直接アクセスできないメンバのことをprivateメンバと言います。
ではどのように設定するか基本構文を以下に紹介します。
書式:privateメンバ
フィールド変数やメソッドにprivate修飾子をつけることにより、他のクラスからのアクセスを禁止し、自身のクラス内からしかアクセスできない特徴を持ちます。
public class クラス名 { private フィールド変数の宣言 … private メソッドの定義 … }
凡例:privateメンバ
前節で示したサンプルのクラス「Computer1」を例にします。
privateメンバは定義するのは至って簡単です。これまでクラスで定義してきたフィールド変数やメソッドの頭にprivateと記述するだけです。では次の項でサンプルを使って説明していきます。
3.1.3 フィールド変数にprivate修飾子をつけたプログラム
privateメンバに直接アクセスし、実際にメンバにはアクセスできないことを確認します。
① ソース・フォルダー: myproj_basic/src
② パッケージ: jp.co.f1.basic.ch03
③ 名前: PrivateMember
④ 作成するメソッド・スタブの選択: public static void main(String[] args) にチェックを入れる
package jp.co.f1.basic.ch03; class Computer2 { private String os; // privateメンバ変数 private int memory; // privateメンバ変数 public void show() { // publicメンバメソッド System.out.println("パソコンのOSは「" + os + "」です。"); System.out.println("メモリサイズは「" + memory + "MByte」です。"); } } public class PrivateMember { public static void main(String[] args) { Computer2 com = new Computer2(); com.os = "WindowsXP"; // privateメンバ変数(os)にアクセス com.memory = -5445; // privateメンバ変数(memory)にアクセス com.show(); } }
実行結果
privateメンバにアクセスするとコンパイルエラーになり実行することができません。
解説
Computer2クラス内の4、5行を見るとフィールド変数(os,memory)がprivateメンバになっています。これによりPrivateMemberクラス内のmain()メソッドの17行と18行で、フィールド変数にはアクセスすることができなくなっています。Computer2 クラスのフィールド変数を「private」にしたことにより他のクラス(ここではPrivateMember)からアクセスすることができなくなります。
図 3.1.1: メンバアクセス制限
メンバをprivateにすることで、不正な値が設定されることはなくなりましたが、このままで良いのでしょうか?
不正な値の設定を防ぐと同時に、値の設定自体もできなくなってしまいました。このままでは値の変更を行いたい場合にアクセスする手段がありません。そのような場合にJavaプログラミングではどのようにして行うかを、次の項で説明していきます。キーワードはこれまで目にしてきている「public」になります。
3.1.4 メソッドにpublicをつける
メンバをprivateにすることで不正なアクセスを禁止できました。しかし正常な値を設定したい場合の手段も同時に失ってしまいました。そのような場合、Javaプログラムでは「正常な値」を「正しい手続きに従い」行える操作(メソッド)を用意します。このメソッドはprivateにしたメンバと、他のクラスからのアクセスを繋ぐためのメソッドになるため、どこからでもアクセスが可能なpublicをつけます。このような他の全てのクラスからアクセスできるメソッドをpublicメソッドと呼びます。
「public」というキーワードはこれまで意識せずに目にしてきている筈ですが、改めて基本構文を紹介します。
メソッドにpublic修飾子をつけることにより、どこからでもアクセスできる特徴を持ちます。
publicメソッドも定義するのは至って簡単です。これまでクラスで定義してきたメソッドの頭にpublicと記述するだけです。では次の項でサンプルを使って説明していきます。
アクセス修飾子について
- これまでの範囲で学習した「private」、「public」を総称してアクセス修飾子と呼びます。この他には「protected」と「デフォルト(省略)」があり、この3つのアクセス修飾子をつけた場合と、デフォルト(省略)した場合ではアクセス制限の範囲が異なります。
3.1.5 メソッドにpublic修飾子をつけたプログラム
publicメソッドを利用して、クラス内のprivateフィールド変数に値を設定します。
直接値を設定することのできないprivateフィールド変数に、publicメソッドを利用することで値を設定できることを確認しましょう。
① ソース・フォルダー: myproj_basic/src
② パッケージ: jp.co.f1.basic.ch03
③ 名前: PublicMember
④ 作成するメソッド・スタブの選択: public static void main(String[] args) にチェックを入れる
package jp.co.f1.basic.ch03; class Computer3 { private String os; private int memory; public void show() { // publicメソッドshow System.out.println("パソコンのOSは「" + os + "」です。"); System.out.println("メモリサイズは「" + memory + "MByte」です。"); } public void setOsMemory(String os, int memory) { // publicメソッドsetOsMemory // 設定するメモリの値が正常か判定 if (memory > 0) { // 正常な引数の値をフィールド変数に設定 this.os = os; this.memory = memory; System.out.println("OSを「" + os + "」にメモリを「" + memory + "MByte」に変更しました。"); } else { System.out.println("「" + memory + "」は正しいメモリサイズではないため、変更は行いません。"); } } } public class PublicMember { public static void main(String[] args) { Computer3 com = new Computer3(); // 他クラス内のprivate変数にはアクセスできない // com.os = "WindowsXP"; //この行はコメントアウトしています // com.memory = -5445; //この行はコメントアウトしています // 正しい値を正式な操作を呼び出して設定 com.setOsMemory("WindowsXP", 2048); // publicメソッドsetOsMemoryを呼び出して値を設定 com.show(); // publicメソッドshowを呼び出す // 不正な値を設定しようとしてみる System.out.println("\nメモリに不正な値(-5445)を指定してみます。"); com.setOsMemory("WindowsXP", -5445); com.show(); } }実行結果
解説
「Computer3」クラスのフィールド変数は「private」になっているので30、31行のような直接アクセスはできません。今回のプログラムではコンパイルエラーを出さない為にコメントアウトしています。
//他クラス内のprivate変数にはアクセスできない //com.os = "WindowsXP"; //この行はコメントアウトしています //com.memory = -5445; //この行はコメントアウトしています
34行でsetOsMemoryメソッドに正しい値「WindowsXP、2048」を引数に与えてアクセスし、35行のshowメソッドで結果を画面に表示しています。このsetOsMemoryメソッドは引数の値をチェックして、問題なければフィールド変数に設定する機能を持っています。
com.setOsMemory("WindowsXP", 2048); com.show();
publicメンバにはどこからでも自由にアクセスができ、privateメンバは同一クラス内の処理からなら自由にアクセスできるため、値の変更、参照が可能になります。
図 3.1.2: アクセス修飾子とメンバアクセスの関係
39行目ではmemoryに設定する値を不正な数値で設定しています。setOsMemoryメソッド内では第2引数のmemory(今回は-5445)値が、「正の値」でなければフィールド変数に設定しないため変更が行われません。
結果40行のshowメソッド呼び出しでは結果が変わっていないことが確認できます。
com.setOsMemory("WindowsXP", -5445); com.show();
今回のサンプルのようにクラスの外から自由にアクセスさせるメンバには「public」、そうでないメンバ(主にフィールド変数)には「private」を設定しました。フィールド変数にはprivateを設定することで、不正なアクセスを防ぎ、フィールド変数に値を設定したければ正規の操作にあたるpublicメソッドを呼び出すことで不正な値も防ぐことができるようになる仕組みです。
このように、クラスの中に属性(フィールド)と機能(メソッド)をひとまとめにし、保護したいメンバに「private」をつけて自由にアクセスできなくする仕組みをカプセル化と呼びます。カプセル化については3.3節で詳しく紹介します。