オブジェクト指向プログラミングにおける重要な概念の一つに、カプセル化があります。 カプセル化 ( encapsulation ) とは、外部からデータの構造が見えないようにし、限られた方法でしかデータにアクセスできないようにすることです。 適切にカプセル化を行いますと、プログラムの作成・変更が容易になります。
カプセル化の本質は情報隠蔽です。 ここで、 情報隠蔽 ( information hiding ) とは、外部に対して必要最小限の情報だけを公開するという考え方です。 公開部分は、外部から利用されますので、簡単には変更できません。 一方、隠蔽部分は、外部から利用されませんので、いつでも変更できます。
関連する情報をひとまとめにしてからカプセル化を行ったプログラム単位は、 モジュール ( module ) と呼ばれます。 プログラムを作成するときに、モジュールを一つ一つ作成するようにしますと、プログラミングがやりやすくなります。
Java言語では、クラスがモジュールの役割を果たします。 つまり、関連する情報は一つのクラスにまとめ、クラスごとにプログラムを作成するということです。 また、情報隠蔽については、クラスのインスタンス変数やインスタンス・メソッドなどが対象となります。 公開するインスタンス変数と隠蔽するインスタンス変数、公開するインスタンス・メソッドと隠蔽するインスタンス・メソッドに分別します。 隠蔽されたインスタンス変数やインスタンス・メソッドを変更しても、他のプログラムは変更しなくてよいのです。
カプセル化の例として、何時何分という時計のクラスを考えます。 クラス名を Clock とし、何時を変数 hour , 何分を変数 minute で表すことにします。 メイン・プログラムでは、13時15分というデータを格納してから取り出します。
/* 1*/ class Clock { /* 2*/ int hour; /* 3*/ int minute; /* 4*/ }
/* 1*/ class ClockMain { /* 2*/ public static void main (String[] args) { /* 3*/ Clock c = new Clock(); /* 4*/ c.hour = 13; /* 5*/ c.minute = 15; /* 6*/ System.out.println(c.hour + ":" + c.minute); /* 7*/ } /* 8*/ }
asiaa1:~/comp3b b08a001$ java ClockMain 13:15 asiaa1:~/comp3b b08a001$
もし、変数名 hour と minute を、それぞれ ji と fun に変更しますと、メイン・プログラムも変えなければなりません。
/* 1*/ class Clock { /* 2*/ int ji; /* 3*/ int fun; /* 4*/ }
/* 1*/ class ClockMain { /* 2*/ public static void main (String[] args) { /* 3*/ Clock c = new Clock(); /* 4*/ c.ji = 13; /* 5*/ c.fun = 15; /* 6*/ System.out.println(c.ji + ":" + c.fun); /* 7*/ } /* 8*/ }
一か所を変更するとすべてを変更しなければならないようでは、プログラムの変更は困難になってしまいます。
ここでカプセル化を行います。
まず、インスタンス変数は隠蔽します。
次に、インスタンス変数へアクセスするメソッドを用意し、これを公開します。
Java言語では、
private
と書きますと、その情報が隠蔽されます。
/* 1*/ class Clock { /* 2*/ private int hour; /* 3*/ private int minute; /* 4*/ void setHour (int hour) { /* 5*/ this.hour = hour; /* 6*/ } /* 7*/ void setMinute (int minute) { /* 8*/ this.minute = minute; /* 9*/ } /* 10*/ int getHour () { /* 11*/ return this.hour; /* 12*/ } /* 13*/ int getMinute () { /* 14*/ return this.minute; /* 15*/ } /* 16*/ }
/* 1*/ class ClockMain { /* 2*/ public static void main (String[] args) { /* 3*/ Clock c = new Clock(); /* 4*/ c.setHour(13); /* 5*/ c.setMinute(15); /* 6*/ System.out.println(c.getHour() + ":" + c.getMinute()); /* 7*/ } /* 8*/ }
カプセル化を行ったことによって、隠蔽部分であるインスタンス変数を変更しても、メイン・プログラムはそのままでよくなります。
/* 1*/ class Clock { /* 2*/ private int ji; /* 3*/ private int fun; /* 4*/ void setHour (int ji) { /* 5*/ this.ji = ji; /* 6*/ } /* 7*/ void setMinute (int fun) { /* 8*/ this.fun = fun; /* 9*/ } /* 10*/ int getHour () { /* 11*/ return this.ji; /* 12*/ } /* 13*/ int getMinute () { /* 14*/ return this.fun; /* 15*/ } /* 16*/ }
/* 1*/ class ClockMain { /* 2*/ public static void main (String[] args) { /* 3*/ Clock c = new Clock(); /* 4*/ c.setHour(13); /* 5*/ c.setMinute(15); /* 6*/ System.out.println(c.getHour() + ":" + c.getMinute()); /* 7*/ } /* 8*/ }
カプセル化を行うときは、どの情報を隠蔽し、どの情報を公開するかを適切に決定することが大事です。
特に、インスタンス変数はすべて
private
にするのが普通です。
今後の予定は次の通りです。
この授業の成績は、レポートの提出と試験の得点で決まります。
成績に関して次のような事情のある人はメールで連絡してください。 できる限り対処します。
問1. 次のプログラムは、配列 {10, 20, 30, 0, 50} の要素をすべて出力します。 ただし、値が 0 の要素が見つかったら、そこで出力を打ち切る(つもりの)ものです。 このプログラムの不具合を修正してください。
/* 1*/ class StopAtZero { /* 2*/ public static void main (String[] args) { /* 3*/ int i; /* 4*/ int[] a = {10, 20, 30, 0, 50}; /* 5*/ for (i = 0; i < a.length; i++) { /* 6*/ if (a[i] == 0) { /* 7*/ continue; /* 8*/ } /* 9*/ System.out.println(a[i]); /* 10*/ } /* 11*/ } /* 12*/ }
asiaa1:~/comp3b b08a001$ java StopAtZero 10 20 30 50 // 50 は出力されてはならない asiaa1:~/comp3b b08a001$
問2. 次のプログラムは、星印を1行目に1個、2行目に2個、..., 10行目に10個出力する(つもりの)ものです。 このプログラムの不具合を修正してください。
/* 1*/ class Asterisk55 { /* 2*/ public static void main (String[] args) { /* 3*/ int i, j; /* 4*/ for (i = 0; i < 10; i++) { /* 5*/ for (j = 0; j < i; j++) { /* 6*/ System.out.print("*"); /* 7*/ } /* 8*/ System.out.println(); /* 9*/ } /* 10*/ } /* 11*/ }
asiaa1:~/comp3b b08a001$ java Asterisk55 // 星印が 1 個足りない * // 星印が 1 個足りない ** // 星印が 1 個足りない *** // 星印が 1 個足りない **** // 星印が 1 個足りない ***** // 星印が 1 個足りない ****** // 星印が 1 個足りない ******* // 星印が 1 個足りない ******** // 星印が 1 個足りない ********* // 星印が 1 個足りない asiaa1:~/comp3b b08a001$
問3. 次のプログラムは、20時30分を表すインスタンス x を生成し、x のコピー y を作成し、y の分のフィールドを 40 に変更する(つもりの)ものです。 このプログラムの不具合を修正してください。
/* 1*/ class Time { /* 2*/ int hour, minute; /* 3*/ }
/* 1*/ class TimeCopy { /* 2*/ public static void main (String[] args) { /* 3*/ Time x, y; /* 4*/ x = new Time(); /* 5*/ x.hour = 20; /* 6*/ x.minute = 30; /* 7*/ y = x; /* 8*/ y.hour = x.hour; /* 9*/ y.minute = x.minute; /* 10*/ y.minute = 40; /* 11*/ System.out.println(x.hour + ":" + x.minute); /* 12*/ System.out.println(y.hour + ":" + y.minute); /* 13*/ } /* 14*/ }
asiaa1:~/comp3b b08a001$ java TimeCopy 20:40 // x は 20:30 のはず 20:40 asiaa1:~/comp3b b08a001$
問4. 次のプログラムは、空のリストを生成し、その先頭に 1 を挿入し、さらにその先頭に 2 を挿入し、さらにその先頭に 3 を挿入して、[3, 2, 1] というリストにする(つもりの)ものです。 このプログラムの不具合を修正してください。
/* 1*/ class ListNode { /* 2*/ int data; /* 3*/ ListNode next; /* 4*/ ListNode (int data, ListNode next) { /* 5*/ this.data = data; /* 6*/ this.next = next; /* 7*/ } /* 8*/ }
/* 1*/ class List321 { /* 2*/ public static void main (String[] args) { /* 3*/ int DUMMY = 0; /* 4*/ ListNode list = new ListNode(DUMMY, null); /* 5*/ list.next = new ListNode(1, list.next); /* 6*/ System.out.print(" " + list.next.data); /* 7*/ System.out.println(); /* 8*/ list.next.next = new ListNode(2, list.next); /* 9*/ System.out.print(" " + list.next.data); /* 10*/ System.out.print(" " + list.next.next.data); /* 11*/ System.out.println(); /* 12*/ list.next.next.next = new ListNode(3, list.next); /* 13*/ System.out.print(" " + list.next.data); /* 14*/ System.out.print(" " + list.next.next.data); /* 15*/ System.out.print(" " + list.next.next.next.data); /* 16*/ System.out.println(); /* 17*/ } /* 18*/ }
asiaa1:~/comp3b b08a001$ java List321 1 1 2 // 正しくは 2 1 1 2 3 // 正しくは 3 2 1 asiaa1:~/comp3b b08a001$
問5. 次のプログラムは、幅 3, 高さ 2 の長方形を生成して、その面積を求める(つもりの)ものです。 このプログラムの不具合を修正してください。
/* 1*/ class Rectangle { /* 2*/ int width = 0; /* 3*/ int height = 0; /* 4*/ void setWidth (int width) { /* 5*/ this.width = this.width; /* 6*/ } /* 7*/ void setHeight (int height) { /* 8*/ this.height = this.height; /* 9*/ } /* 10*/ int getArea () { /* 11*/ return this.width * this.height; /* 12*/ } /* 13*/ }
/* 1*/ class RectangleMain { /* 2*/ public static void main (String[] args) { /* 3*/ Rectangle rectangle = new Rectangle(); /* 4*/ rectangle.setWidth(3); /* 5*/ rectangle.setHeight(2); /* 6*/ System.out.println(rectangle.getArea()); /* 7*/ } /* 8*/ }
asiaa1:~/comp3b b08a001$ java RectangleMain 0 // 正しくは 6 asiaa1:~/comp3b b08a001$
今日の演習12の答案(Javaプログラム)をメールで提出してください。 差出人は学内のメール・アドレス(b08a001@cis.twcu.ac.jpなど)とし、宛先はkonishi@cis.twcu.ac.jpとします。 メールの本文には、学生番号、氏名、科目名、授業日(12月19日)を明記してください。