カプセル化
今まで作成したサンプルは、危険が潜んでいます。それは、スピードやガソリン残量を直接指定することができるという点です。急発進や、急停車、走行中にもかかわらず、ガソリン残量を変更できてしまいます。このままでは、安全なオブジェクトとは言えません。
そこで、オブジェクト指向の特徴のひとつである「カプセル化」を使用して、安全なクラスを作成しましょう。クラスを定義する際に、アクセス修飾子を指定することにより、カプセル化を行うことができます。アクセス修飾子は次のような指定ができます。
アクセス修飾子 | 意味 |
---|---|
public | 同一パッケージ、クラスの内部・外部問わず、自由にアクセスすることができます。アクセス制限のレベルが最も低い修飾子です。 |
protected | 同一パッケージ、クラスの内部と、派生したクラスからはアクセスすることができます。 |
(省略) | 同一パッケージ内からであれば、自由にアクセスすることができます。 |
private | クラスの内部からしかアクセスすることができません。アクセス制限のレベルが最も高い修飾子です。 |
○ プログラム
次のようにCarクラスを編集してください。
Car.java
- public class Car{
- //フィールド
- //public int speed; //スピード情報
- private int speed; //スピード情報(修正)
- //public double gas; //ガソリン情報
- private double gas; //ガソリン情報(修正)
- //メソッド
- //加速させるメソッド
- public void speedUp(int sp){
- //ガソリンを減らす
- this.gas -= sp / 10.0;
- if(this.gas < 0){ //ガソリンがマイナスになる場合
- //ガソリンを0に補正し、スピードを増やさない
- this.gas = 0;
- }else{
- //スピードを増やす
- this.speed += sp;
- }
- }
○ クラス図
○ コンパイル
C:¥work>javac CarTest.java
CarTest.java:6: エラー: speedはCarでprivateアクセスされます
System.out.println("スピード:" + car.speed + "km");
^
CarTest.java:7: エラー: gasはCarでprivateアクセスされます
System.out.println("ガソリン:" + car.gas + "L");
^
CarTest.java:15: エラー: speedはCarでprivateアクセスされます
car.speed = 0;
^
CarTest.java:16: エラー: gasはCarでprivateアクセスされます
car.gas = 20.0;
^
エラー4個
C:¥work>
○ 解説
3行目と6行目のフィールドのアクセス修飾子は「public」を指定していたため、外部のクラスから直接アクセスできました。今回は、カプセル化をするために、一旦これらの行をコメントアウトしてプログラムから外しています。4行目と7行目でアクセス修飾子を「private」に変更して隠ぺいすることにより、外部のクラスからアクセスすることができなくなります。
すると、Carクラスを利用する記述の部分で変化が生じます。フィールドに値を代入したり、参照したりする記述ができなくなります。「CarTest.java」をコンパイルすると、フィールドを利用している行でエラーが発生することを確認できます。エラーを回避するには、メソッドとして値を代入したり、参照したりする機能を追加しなくてはなりません。
○ プログラム
では、フィールドにアクセスできるようにメソッドを追加しましょう。次のようにCarクラスにメソッドを追加してください。
Car.java
- public class Car{
- //フィールド
- //public int speed; //スピード情報
- private int speed; //スピード情報(修正)
- //public double gas; //ガソリン情報
- private double gas; //ガソリン情報(修正)
- //メソッド
- //加速させるメソッド
- public void speedUp(int sp){
- }
- //減速させるメソッド
- public void speedDown(int sp){
- //スピードを減らす
- this.speed -= sp;
- if(this.speed < 0){ //スピードがマイナスになる場合
- //スピードを0に補正する
- this.speed = 0;
- }
- }
- //初期化を行うメソッド
- public void init(double gas){
- this.speed = 0;
- this.gas = gas;
- }
- //スピードを取得する
- public int getSpeed(){
- return this.speed;
- }
- //ガソリン量を取得する
- public double getGas(){
- return this.gas;
- }
- }
○ クラス図
○ 解説
Carクラスを利用するクラスからフィールドに代入することで初期化をしていましたが、privateアクセス修飾子によりフィールドにアクセスできなくなったために、34~37行目では、フィールドを初期化する手続き(メソッド)を用意しました。引数にガソリン量を受け取り、スピード情報を「0km」に、ガソリン量を引数のデータで初期化しています。
また、Carクラスを利用するクラスでスピード情報やガソリン情報を画面に表示する時、フィールドを直接参照して、スピード情報やガソリン量を取得していましたが、隠ぺいによりフィールド変数にアクセスできなくなったために、40~42行目ではスピードを戻り値として返す手続きを、45~47行目ではガソリン量を戻り値として返す手続きを用意しました。 このように、情報は隠蔽し、それらを操作するメソッドを準備することでカプセル化を実現させています。
○ プログラム
では、Carオブジェクトを生成して利用しているプログラムも修正しましょう。
CarTest.java
- import java.io.*;
- public class CarTest{
- //車の状態を表示するメソッド
- public static void showData(Car car){
- //System.out.println("スピード:" + car.speed + "km");
- System.out.println("スピード:" + car.getSpeed() + "km");
- //System.out.println("ガソリン:" + car.gas + "L");
- System.out.println("ガソリン:" + car.getGas() + "L");
- }
- public static void main(String[] args) throws Exception{
- //インスタンスを生成する
- Car car = new Car();
- //フィールド変数を初期化する
- //car.speed = 0;
- //car.gas = 20.0;
- car.init(20.0);
- //現在の状態を表示する
- showData(car);
- }
- }
○ 解説
6、9行目では、Carクラスのカプセル化によりフィールドにアクセスできなくなったため、エラーとなります。そのため、コメントアウトしています。その代わりに7、10行目で新たに手続きとして用意されたスピードやガソリン量を取得するメソッドを呼び出しています。
18、19行目では、Carクラスのカプセル化によりフィールド変数にアクセスできなくなったためにエラーとなります。そのため、コメントアウトしています。その代わりに20行目で新たに手続きとして用意された初期化を行なうinitメソッドを呼び出しています。
一般的に、フィールドはprivate指定で非公開にして、メソッドをpublicで公開にします。ただし、内部処理などで使うメソッドはprivateにして、外部から利用されないようにします。