初心者入門レベルの【Javaの勉強】クラスのカプセル化
2020/04/02
前回はオブジェクト指向を念頭に置いて、Javaのプログラムを分割して「クラス」を作成する方法やその使い方を勉強しました。
ただプログラムを分割して「クラス」を作るだけでは、まだオブジェクト指向として十分とは言えません。
今回は、さらにオブジェクト指向に沿ったプログラムを作るために、「カプセル化」について勉強してみます。
「カプセル化」とは
「カプセル化」というのは、「クラス」の中の変数やメソッドへの外部からのアクセスを制御する方法です。
そもそも「カプセル化」とは、簡単な処理の組合せで複雑な処理を構成してプログラムの見通しを良くするために考え出された構造化プログラミングの手法から生まれたものです。
この構造化プログラミングの利用には、プログラムのバグが減ったり、メンテナンスや再利用がしやすいというメリットがあります。
そのやり方として、プログラミングを小さな処理単位で分割し、他のアプリケーションでも利用することを前提にして関数やモジュールを作りますが、このような手法をプログラムの部品化と呼びます。
そうやって作った再利用しやすいプログラムがいくつもあれば、開発効率の大きな向上が期待できるでしょう。
ただしプログラムの部品化の際には、ほかのモジュールからの独立性を高くすることが重要です。
例えば「グローバル変数」のような外部のどこからでもアクセスできる変数を使用するプログラムは、その変数を介して他のプログラムとの依存関係が強くなり、独立性が低くなってしまいます。
構造化プログラミングの考えでは、こういったプログラムの独立性を損なう方法はできるだけ利用しないよう気をつける必要があります。
Javaにおける「クラス」の「カプセル化」
Javaの場合、この構造化プログラミングをオブジェクト指向に取り入れて、「クラス」の「カプセル化」という方法で実現しています。
つまり「クラス」の「カプセル化」を行うことで、外部からの不用意なアクセスを遮断して、「クラス」の独立性を高めているわけです。
それでは早速プログラムの例として、以下の「クラス」を作ってみましょう。
class CountClass01 {
void showCount() {
System.out.print(1);
}
}
これは数をカウントする「クラス」ですが、まずは数を表示する「メソッド」を宣言・定義しています。
次にこの「クラス」を利用するために、以下のテスト用プログラムを作ります。
public class TestCount01 {
public static void main(String[] args) {
CountClass01 cnt = new CountClass01();
System.out.print("現在のカウントは");
cnt.showCount();
System.out.print("です。");
}
}
これによって“CountClass01”をインスタンス化し、その中の「メソッド」“showCount”を呼び出しており、実行してみると次のようになります。
>java TestCount01
現在のカウントは1です。
>
続いてカウントを増やすために、以下のようにプログラムを変更してみました。
class CountClass02 {
void showCount(int addNumber) {
int count = 1;
count = count + addNumber;
System.out.print(count);
}
}
このプログラムでは、メソッドにパラメータとしてカウントに加える値を渡し、カウント表示をする前にその値を加算するという処理を行っています。
それでは、以下のテスト用プログラムを作って変更後の「クラス」を呼び出してみましょう。
public class TestCount02 {
public static void main(String[] args) {
CountClass02 cnt = new CountClass02();
System.out.print("現在のカウントは");
cnt.showCount(1);
System.out.println("です。");
}
}
これを実行してみると、確かにカウントは指定した値だけ加算されます。
しかし何度か呼び出してみると、毎回カウントが初期値の“1”に戻ってしまい、加算後の値が保存されないことが分かります。
これは「クラス」の中の変数“count”が「メソッド」内のローカル変数で、「メソッド」が呼び出されるたびにメモリ上に作成され、「メソッド」が呼び出された後はメモリ上から削除されてしまうためです。
それでは変数の値を保持するようにするために、プログラムを以下のように変更してみましょう。
class CountClass03 {
int count = 1;
void showCount(int addNumber) {
count = count + addNumber;
System.out.print(count);
}
}
変更後のプログラムでは、変数がオブジェクトの属性を表す「インスタンス変数(フィールド)」で宣言されており、「インスタンス変数」がメモリ上にある限りその値が保存されます。
試しに、以下のテスト用プログラムで変更後のプログラムを呼び出してみましょう。
public class TestCount03 {
public static void main(String[] args) {
CountClass03 cnt = new CountClass03();
System.out.print("現在のカウントは");
cnt.showCount(1);
System.out.println("です。");
System.out.print("現在のカウントは");
cnt.showCount(1);
System.out.println("です。");
System.out.print("現在のカウントは");
cnt.showCount(1);
System.out.println("です。");
}
}
このプログラムを実行してみると次のようになって、呼び出した後の変数の値が保持されていることが分かります。
>java TestCount03
現在のカウントは2です。
現在のカウントは3です。
現在のカウントは4です。
>
ただし、このような「インスタンス変数」はスコープが広いため、外部から直接アクセスすることができて独立性が低いものになっています。
それでは、このプログラムの独立性を高くして構造化プログラミングに沿った形にするにはどうするかというと、隠蔽という方法を使います。
例えば以下のように変数“count”の宣言の際に“private”を付けてやると他のクラスから直接アクセスすることができなくなりますが、こういった方法で「クラス」の内部データを隠蔽することを「カプセル化」と呼びます。
class CountClass04 {
private int count = 1;
void showCount(int addNumber) {
count = count + addNumber;
System.out.print(count);
}
}
この“private”修飾子を付けた「インスタンス変数」も、そのインスタンスの内部からなら価を変えることができるので、それを利用して「カプセル化」した「インスタンス変数」に外部から安全にアクセスできるようにする方法が「アクセサ」です。
「アクセサ」は、“private”な「インスタンス変数」へ値を代入する「メソッド」と、「インスタンス変数」の値を返す「メソッド」の2つで構成されますが、当然どちらの「メソッド」も“public”で宣言して外部の「クラス」から利用できるようにする必要がある点に注意しましょう。
例として、上のプログラムにアクセサを追加したのが、以下のプログラムになります。
class CountClass05 {
private int count = 1;
void showCount(int addNumber) {
count = count + addNumber;
System.out.print(count);
}
public void setCount(int i) {
count = i;
}
public int getCount() {
return count;
}
}
“setCount”の「メソッド」は“int型”の引数を持ち、その引数をそのまま変数“count”に代入して、“getCount”の「メソッド」は、変数“count”の値をそのまま返しています。
このプログラムで、外部から「メソッド」を介して「インスタンス変数」にアクセスするための、ごく基本的な「アクセサ」の働きが理解できると思います。
ただこの“setCount”ように渡された値を代入するだけの「メソッド」では、直接“count”にアクセスしているのとあまり変わりが無く、その安全性に問題があります。
そこで、例えば“count”に負の値を入れることができないというルールを作って、代入する前にそのチェックができるようにしたのが以下のプログラムになります。
class CountClass06 {
private int count = 1;
void showCount(int addNumber) {
count = count + addNumber;
System.out.print(count);
}
public void setCount(int i) {
if(i < 0) {
System.out.println("0以上の値をセットしてください。");
} else {
count = i;
}
}
public int getCount() {
return count;
}
}
それでは、このプログラムを以下のプログラムで呼び出してみましょう。
public class TestCount04 {
public static void main(String[] args) {
CountClass06 cnt = new CountClass06();
System.out.println("現在のカウントは" + cnt.getCount() + "です。");
cnt.setCount(-10);
System.out.println("現在のカウントは" + cnt.getCount() + "です。");
cnt.setCount(10);
System.out.println("現在のカウントは" + cnt.getCount() + "です。");
}
}
このプログラムの実行結果は、以下のようになります。
>java TestCount04
現在のカウントは1です。
0以上の値をセットしてください。
現在のカウントは1です。
現在のカウントは10です。
>
まとめ
今回は、Javaの「クラス」の「カプセル化」について、“private”修飾子や「アクセサ」について勉強してみました。
こういった「カプセル化」によるプログラムの作成は、個人レベルの小さなプログラムの開発ではあまりその効果は感じられませんが、ある程度規模の大きいシステムを複数のプログラマによって開発するレベルになると重要になってきます。
ですから小さなプログラムを作るときでも、他人が使えるような独立性や安全性の高いプログラムを作るように、普段から意識しておくのは大事なことといえるでしょうね。
それでは、今回はここまでです。
《 【Javaの勉強】一覧 》