修飾子でデータを守る

クラスを用いることで,データとデータ操作をひとまとめにすることができた.これによって,クラス内に定義したデータに対して,クラス内から操作を行うように決めることができる.しかし,現状では,決められた操作以外に,クラス外から直接にデータを変更することができる.

試しに,リストのように追加してほしい.

class Main
{
    public static void main(String[] args)
    {
        Stack tower = new Stack(10);
        tower.push(10);
        tower.push(20);
        tower.push(30);
        tower.push(40);
        tower.push(50);
        tower.push(60);
        tower.data[3] = 100;
        System.out.println(tower.pop());
        System.out.println(tower.pop());
        System.out.println(tower.pop());
        System.out.println(tower.pop());
        System.out.println(tower.pop());
        System.out.println(tower.pop());
    }
}

追加した状態で実行すると,以下の実行結果のような結果となる.本来,スタックでは,popとpush以外の操作を受け付けないことで,データ操作の順序が守られていた.順序が守られていることで,スタックのデータ構造を生かすことができた.しかし,途中にあるデータを直接操作できるようでは,データの完全性が保証されない結果となる.そこで,決められた操作以外を受け付けないような仕組みを設定する.

5個分のスタック生成
スタックサイズは5
10個分のスタック生成
スタックサイズは10
|10|0|0|0|0|
|10|20|0|0|0|
|10|20|30|0|0|
|10|20|30|40|0|
|10|20|30|40|50|
50
|10|20|30|100|0|
100
|10|20|30|0|0|
30
|10|20|0|0|0|
20
|10|0|0|0|0|
10
|0|0|0|0|0|
-1
|0|0|0|0|0|

そこで,Stackクラスのフィールドにprivate修飾子をつける.

public class Stack
{
    private int volume;
    private int data[];
    //標準サイズのためのコンストラクタ
    Stack()
    {
        this(defaultSize);
    }
    //サイズ指定のためのコンストラクタ
    Stack(int stackSize)
    {
        data = new int[stackSize];
        System.out.println(data.length + "個分のスタック生成");
    }
    //データ追加メソッド
    boolean push(int number)
    {
        if(volume < data.length)
        {
            data[volume] = number;
            volume++;
            return true;
        }
        else
        {
            System.out.println("stack overflow");
            return false;
        }
    }
    //データ取得メソッド
    int pop()
    {
        int value;
        if(volume > 0)
        {
            value = data[volume -1];
            data[volume -1] = 0;
            volume--;
        }
        else
        {
            value = -1;
        }
        return value;
    }
    //状態表示メソッド
    void printStack()
    {
        System.out.print("|");
        for(int i=0; i < data.length; i++)
        {
            System.out.print(data[i]);
            System.out.print("|");
        }
        System.out.println("");
    }
    //個別の状態表示メソッド
    void printStack(int i)
    {
        System.out.print("|");
        System.out.print(data[i]+"|");
        System.out.println("");
    }
}

この状態で,ソースコードの表示を確認すると,Mainクラスのmainメソッドからデータに直接アクセスしようとする命令において,「不可視」であるとの警告がでることが確認できる.


すなわち,直接データを触れることが妨げられていて,データが守られていることがわかる.

変数につけられる修飾子は以下である.
final定数として使える
private同じクラスのコードからのみアクセスできる
protectedサブクラスからアクセスできる
public他のクラスからアクセスできる
static静的変数
(なし)同じパッケージの範囲でアクセスできる

アクセスできる範囲を修飾子で指定できるのは,データをもつフィールドだけでなく,データを操作するメソッドについても同様である.privateを指定したメソッドについては,クラスの外から実行することができない.

このように,クラスの中を他のクラスから守ることをカプセル化と呼ぶ.この「カプセル」は,お薬の「カプセル」と同じことを意味する.お薬の場合は,カプセル内の薬を外から守っている.クラスの場合は,クラス内のデータであるフィールド,クラス内の操作であるメソッドを,クラス外から保護するために「カプセル化」する.

演習

キューのプログラムにおいて,キューのデータを用意したメソッド以外からアクセスできないように,修飾子を適切に設定せよ.設定した際には,外部からデータにアクセスできないことを確認するステートメントを記載して試すこと.確認後はコメントアウトしておくとよい.

修飾子でデータを守る