いくつかのデータをひとまとめにしたデータ構造をレコード(または構造体)と呼ぶこと、および、Javaではレコードの代わりにインスタンスを用いることを思い出してください。 そのときの例では、時刻を表すクラスTimeに関する、次のようなプログラムを動かしました。
/* 1*/ class Time { /* 2*/ int hour, minute; /* 3*/ }
/* 1*/ class TimeTest { /* 2*/ public static void main (String[] args) { /* 3*/ Time x; /* 4*/ x = new Time(); /* 5*/ x.hour = 9; /* 6*/ x.minute = 30; /* 7*/ System.out.println(x.hour + ":" + x.minute); /* 8*/ } /* 9*/ }
asiaa1:~/comp3b b08a001$ java TimeTest 9:30 asiaa1:~/comp3b b08a001$
このプログラムで、変数 x には何が格納されるでしょうか。 変数にはデータが一つしか格納できませんので、インスタンスそのものは入りません。 実は、 x には参照とよばれるデータが格納されます。
参照 ( reference ) とは、メモリの中でデータが配置されている場所のことです。 変数 x には、インスタンスが置かれている「場所データ」が格納されるのです。 参照をポインタやアドレスのようなものだと思っても差し支えありません。 参照を、WWWにおけるURLのようなものだと考えてもよいでしょう。 参照は、しばしば下図のような矢印で書き表わされます。
上記のプログラムは、変数を宣言し、インスタンスを生成してその変数に代入し、そしてそのフィールドに数を代入しています。 この処理の流れを順に表現したのが以下の図です。
コンストラクタというものを利用すると、インスタンスの生成とフィールドへの代入が一度に行えました。 そのときの例では、チャイムの時刻を表すクラスChimeに関する、次のようなプログラムを動かしました。
/* 1*/ class Chime { /* 2*/ int hour, minute; /* 3*/ Chime (int hour, int minute) { /* 4*/ this.hour = hour; /* 5*/ this.minute = minute; /* 6*/ } /* 7*/ }
/* 1*/ class ChimeTest { /* 2*/ public static void main (String[] args) { /* 3*/ Chime c = new Chime(10, 30); /* 4*/ System.out.println(c.hour + ":" + c.minute); /* 5*/ } /* 6*/ }
asiaa1:~/comp3b b08a001$ java ChimeTest 10:30 asiaa1:~/comp3b b08a001$
プログラムの上では、一気にデータが出来上がるように見えますが、実際には、上の図のように段階的にデータが構成されます。
インスタンスと同様、変数に配列を代入しても、その変数にはその配列への参照が格納されます。 例えば、
int[] a = {91, 74, 57, 40, 23};
のように配列を初期化すると、次の図のようになります。
以下では、主にインスタンスへの参照について説明します。 配列への参照についても同じことが言えます。
参照の中には、 null参照 とよばれる特別なデータがあります。 これは、「どこも参照していない」という参照です。
以下のプログラムでは、変数 x にnull参照を格納し、どこも参照しないようにしています。
/* 1*/ class NullTest { /* 2*/ public static void main (String[] args) { /* 3*/ Time x; /* 4*/ x = null; /* 5*/ System.out.println(x); /* 6*/ } /* 7*/ }
asiaa1:~/comp3b b08a001$ java NullTest null asiaa1:~/comp3b b08a001$
参照が格納される変数の値を変えることは、参照先を変更することになります。
次のプログラムでは、まず10時45分を表すインスタンスへの参照が変数 x に格納されます。 そして x の値は、11時15分を表すインスタンスへの参照に置き換わります。
/* 1*/ class TimeTest2 { /* 2*/ public static void main (String[] args) { /* 3*/ Time x; /* 4*/ x = new Time(); /* 5*/ x.hour = 10; /* 6*/ x.minute = 45; /* 7*/ System.out.println(x.hour + ":" + x.minute); /* 8*/ x = new Time(); /* 9*/ x.hour = 11; /* 10*/ x.minute = 15; /* 11*/ System.out.println(x.hour + ":" + x.minute); /* 12*/ } /* 13*/ }
asiaa1:~/comp3b b08a001$ java TimeTest2 10:45 11:15 asiaa1:~/comp3b b08a001$
同じことを、コンストラクタを利用して行うと、以下のようになります。
/* 1*/ class ChimeTest2 { /* 2*/ public static void main (String[] args) { /* 3*/ Chime x; /* 4*/ x = new Chime(10, 45); /* 5*/ System.out.println(x.hour + ":" + x.minute); /* 6*/ x = new Chime(11, 15); /* 7*/ System.out.println(x.hour + ":" + x.minute); /* 8*/ } /* 9*/ }
参照が格納される変数には、新しく生成されるインスタンスだけではなく、すでに生成されたインスタンスも代入できます。 この場合、その変数にはそのインスタンスへの参照のコピーが格納されます。 インスタンスそのものはコピーされません。
また、整数(
int
型)どうしでは、関係演算子として
>
や
==
などが使えましたが、インスタンスどうしでは
==
が使えます。
これは参照先が一致していることを意味します。
フィールドの値がすべて等しくても、置かれている場所が異なれば、インスタンスは(
==
の意味で)等しくありません。
次のプログラムは、まず変数 x に12時30分を表すインスタンスへの参照を格納します。 そして、変数 y にはそのインスタンスを直接代入し、変数 z にはフィールドごとに代入します。 x と y は等しいインスタンスだと判定されますが、 x と z は等しくないと判定されます。
/* 1*/ class TimeTest3 { /* 2*/ public static void main (String[] args) { /* 3*/ Time x, y, z; /* 4*/ x = new Time(); /* 5*/ x.hour = 12; /* 6*/ x.minute = 30; /* 7*/ System.out.println(x.hour + ":" + x.minute); /* 8*/ y = x; /* 9*/ System.out.println(y.hour + ":" + y.minute); /* 10*/ z = new Time(); /* 11*/ z.hour = x.hour; /* 12*/ z.minute = x.minute; /* 13*/ System.out.println(z.hour + ":" + z.minute); /* 14*/ if (x == y) { /* 15*/ System.out.println("xとyは等しい"); /* 16*/ } else { /* 17*/ System.out.println("xとyは等しくない"); /* 18*/ } /* 19*/ if (x == z) { /* 20*/ System.out.println("xとzは等しい"); /* 21*/ } else { /* 22*/ System.out.println("xとzは等しくない"); /* 23*/ } /* 24*/ } /* 25*/ }
asiaa1:~/comp3b b08a001$ java TimeTest3 12:30 12:30 12:30 xとyは等しい xとzは等しくない asiaa1:~/comp3b b08a001$
同じことを、コンストラクタを利用して行うと、以下のようになります。
/* 1*/ class ChimeTest3 { /* 2*/ public static void main (String[] args) { /* 3*/ Chime x, y, z; /* 4*/ x = new Chime(12, 30); /* 5*/ System.out.println(x.hour + ":" + x.minute); /* 6*/ y = x; /* 7*/ System.out.println(y.hour + ":" + y.minute); /* 8*/ z = new Chime(x.hour, x.minute); /* 9*/ System.out.println(z.hour + ":" + z.minute); /* 10*/ if (x == y) { /* 11*/ System.out.println("xとyは等しい"); /* 12*/ } else { /* 13*/ System.out.println("xとyは等しくない"); /* 14*/ } /* 15*/ if (x == z) { /* 16*/ System.out.println("xとzは等しい"); /* 17*/ } else { /* 18*/ System.out.println("xとzは等しくない"); /* 19*/ } /* 20*/ } /* 21*/ }
時刻を表すクラスTimeも、チャイムの時刻を表すクラスChimeも、そのフィールドは整数(int型)でした。 フィールドがインスタンスであるクラスも考えられます。 この場合、参照の構造は多少複雑になります。
以下では、クラスSpanを定義しています。 これは、何時何分から何時何分までという時間帯を表します。 このクラスのフィールドは、クラスTimeのインスタンスです。 from というフィールドには開始時刻、 to というフィールドには終了時刻を格納します。
/* 1*/ class Span { /* 2*/ Time from, to; /* 3*/ }
それでは、10:55から12:25までという時間帯のデータを構成します。 参照の構造は次のようになります。
+-----+ | | | o--+-----+ +-----+ | x | v +-----+-----+ | | | +-----+--o | o--+-----+ | +-----+-----+ | | from to | v v +-----+-----+ +-----+-----+ | | | | | | | 10 | 55 | | 12 | 25 | +-----+-----+ +-----+-----+ hour minute hour minute
この構造を、下から上に向かって構成すると、以下のようになります。 いったん、変数 y , z に参照を格納していることに注意してください。
/* 1*/ class SpanTest { /* 2*/ public static void main (String[] args) { /* 3*/ Span x; /* 4*/ Time y, z; /* 5*/ y = new Time(); /* 6*/ y.hour = 10; /* 7*/ y.minute = 55; /* 8*/ z = new Time(); /* 9*/ z.hour = 12; /* 10*/ z.minute = 25; /* 11*/ x = new Span(); /* 12*/ x.from = y; /* 13*/ x.to = z; /* 14*/ System.out.print(x.from.hour + ":"); /* 15*/ System.out.print(x.from.minute + "--"); /* 16*/ System.out.print(x.to.hour + ":"); /* 17*/ System.out.println(x.to.minute); /* 18*/ } /* 19*/ }
asiaa1:~/comp3b b08a001$ java SpanTest 10:55--12:25 asiaa1:~/comp3b b08a001$
+-----+ | | | o--+-----+ +-----+ | x | v +-----+-----+ | | | +-----+--o | o--+-----+ | +-----+-----+ | | from to | v v +-----+ +-----+-----+ +-----+-----+ +-----+ | | | | | | | | | | | o--+---->| 10 | 55 | | 12 | 25 |<----+--o | +-----+ +-----+-----+ +-----+-----+ +-----+ y hour minute hour minute z
逆に、上から下に向かって構成すると、以下のようになります。
/* 1*/ class SpanTest2 { /* 2*/ public static void main (String[] args) { /* 3*/ Span x = new Span(); /* 4*/ x.from = new Time(); /* 5*/ x.from.hour = 10; /* 6*/ x.from.minute = 55; /* 7*/ x.to = new Time(); /* 8*/ x.to.hour = 12; /* 9*/ x.to.minute = 25; /* 10*/ System.out.print(x.from.hour + ":"); /* 11*/ System.out.print(x.from.minute + "--"); /* 12*/ System.out.print(x.to.hour + ":"); /* 13*/ System.out.println(x.to.minute); /* 14*/ } /* 15*/ }
次は、フィールドがインスタンスであるクラスで、コンストラクタを定義する例です。
以下では、クラスLectureを定義しています。 これは、何時何分のチャイムから何時何分のチャイムまでという講義時間を表します。 このクラスのフィールドは、クラスChimeのインスタンスです。 from というフィールドには開始時刻のチャイム、 to というフィールドには終了時刻のチャイムを格納します。
/* 1*/ class Lecture { /* 2*/ Chime from, to; /* 3*/ Lecture (Chime from, Chime to) { /* 4*/ this.from = from; /* 5*/ this.to = to; /* 6*/ } /* 7*/ }
下から上に向かって構成すると、以下のようになります。
/* 1*/ class LectureTest { /* 2*/ public static void main (String[] args) { /* 3*/ Lecture x; /* 4*/ Chime y, z; /* 5*/ y = new Chime(10, 55); /* 6*/ z = new Chime(12, 25); /* 7*/ x = new Lecture(y, z); /* 8*/ System.out.print(x.from.hour + ":"); /* 9*/ System.out.print(x.from.minute + "--"); /* 10*/ System.out.print(x.to.hour + ":"); /* 11*/ System.out.println(x.to.minute); /* 12*/ } /* 13*/ }
上から下に向かって構成すると、以下のようになります。 いったん、null参照を使ってコンストラクタを呼び出していることに注意してください。
/* 1*/ class LectureTest2 { /* 2*/ public static void main (String[] args) { /* 3*/ Lecture x = new Lecture(null, null); /* 4*/ x.from = new Chime(10, 55); /* 5*/ x.to = new Chime(12, 25); /* 6*/ System.out.print(x.from.hour + ":"); /* 7*/ System.out.print(x.from.minute + "--"); /* 8*/ System.out.print(x.to.hour + ":"); /* 9*/ System.out.println(x.to.minute); /* 10*/ } /* 11*/ }
「7月21日から8月31日までの休み」という意味のデータを構成してください。 参照の構造は以下の通りとします。
+-----+ | | | o--+-----+ +-----+ | x | v +-----+-----+ | | | +-----+--o | o--+-----+ | +-----+-----+ | | from to | v v +-----+-----+ +-----+-----+ | | | | | | | 7 | 21 | | 8 | 31 | +-----+-----+ +-----+-----+ month day month day
まず、日付を表すクラスDateを定義します。 フィールドmonthには月を、フィールドdayには日を格納します。 コンストラクタは定義してもしなくてもよいです。
次に、休みを表すクラスVacationを定義します。 フィールドfromには開始の日付を、フィールドtoには終了の日付を格納します。 コンストラクタは定義してもしなくてもよいです。
最後に、次のプログラムの穴埋めをして、構成したデータの内容が確認できるようにしてください。 構成の手順は、上から下へでも、下から上へでもどちらでもよいです。
class VacationMain { public static void main (String[] args) { ??? System.out.print(x.from.month + "/"); System.out.print(x.from.day + "--"); System.out.print(x.to.month + "/"); System.out.println(x.to.day); } }
asiaa1:~/comp3b b08a001$ java VacationMain 7/21--8/31 asiaa1:~/comp3b b08a001$
今日の演習5の答案(Javaプログラム)をメールで提出してください。 差出人は学内のメール・アドレス(b08a001@cis.twcu.ac.jpなど)とし、宛先はkonishi@cis.twcu.ac.jpとします。 メールの本文には、学生番号、氏名、科目名、授業日(10月24日)を明記してください。