[小西ホームページ]   [目次・索引]   [前の授業]   [次の授業]

コンピュータIIIB(Javaアルゴリズム)第5回

目次
5.1 参照(1)
5.1.1 参照とは
5.1.2 参照と配列
5.1.3 null参照
5.1.4 インスタンスの代入
5.1.5 インスタンスの比較とコピー
5.1.6 インスタンスのフィールド
5.2 演習5
5.3 レポート課題
5.4 参考文献
索引
参照  

5.1 参照(1)

5.1.1 参照とは

いくつかのデータをひとまとめにしたデータ構造をレコード(または構造体)と呼ぶこと、および、Javaではレコードの代わりにインスタンスを用いることを思い出してください。 そのときの例では、時刻を表すクラスTimeに関する、次のようなプログラムを動かしました。

Time.java
/*  1*/ class Time {
/*  2*/     int hour, minute;
/*  3*/ }
TimeTest.java
/*  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のようなものだと考えてもよいでしょう。 参照は、しばしば下図のような矢印で書き表わされます。

参照のイメージ
図 5.1  参照のイメージ

上記のプログラムは、変数を宣言し、インスタンスを生成してその変数に代入し、そしてそのフィールドに数を代入しています。 この処理の流れを順に表現したのが以下の図です。

参照の格納
参照の格納
参照の格納
図 5.2  参照の格納

コンストラクタというものを利用すると、インスタンスの生成とフィールドへの代入が一度に行えました。 そのときの例では、チャイムの時刻を表すクラスChimeに関する、次のようなプログラムを動かしました。

Chime.java
/*  1*/ class Chime {
/*  2*/     int hour, minute;
/*  3*/     Chime (int hour, int minute) {
/*  4*/         this.hour = hour;
/*  5*/         this.minute = minute;
/*  6*/     }
/*  7*/ }
ChimeTest.java
/*  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$

プログラムの上では、一気にデータが出来上がるように見えますが、実際には、上の図のように段階的にデータが構成されます。

5.1.2 参照と配列

インスタンスと同様、変数に配列を代入しても、その変数にはその配列への参照が格納されます。 例えば、

int[] a = {91, 74, 57, 40, 23};

のように配列を初期化すると、次の図のようになります。

配列への参照
図 5.3  配列への参照

以下では、主にインスタンスへの参照について説明します。 配列への参照についても同じことが言えます。

5.1.3 null参照

参照の中には、 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$

5.1.4 インスタンスの代入

参照が格納される変数の値を変えることは、参照先を変更することになります。

次のプログラムでは、まず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$
参照の変更
図 5.4  参照の変更

同じことを、コンストラクタを利用して行うと、以下のようになります。

/*  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*/ }

5.1.5 インスタンスの比較とコピー

参照が格納される変数には、新しく生成されるインスタンスだけではなく、すでに生成されたインスタンスも代入できます。 この場合、その変数にはそのインスタンスへの参照のコピーが格納されます。 インスタンスそのものはコピーされません。

また、整数( int 型)どうしでは、関係演算子として >== などが使えましたが、インスタンスどうしでは == が使えます。 これは参照先が一致していることを意味します。 フィールドの値がすべて等しくても、置かれている場所が異なれば、インスタンスは( == の意味で)等しくありません。

次のプログラムは、まず変数 x に12時30分を表すインスタンスへの参照を格納します。 そして、変数 y にはそのインスタンスを直接代入し、変数 z にはフィールドごとに代入します。 xy は等しいインスタンスだと判定されますが、 xz は等しくないと判定されます。

/*  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$
インスタンスの比較
図 5.5  インスタンスの比較

同じことを、コンストラクタを利用して行うと、以下のようになります。

/*  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*/ }

5.1.6 インスタンスのフィールド

時刻を表すクラス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*/ }

5.2 演習5

「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.3 レポート課題

今日の演習5の答案(Javaプログラム)をメールで提出してください。 差出人は学内のメール・アドレス(b08a001@cis.twcu.ac.jpなど)とし、宛先はkonishi@cis.twcu.ac.jpとします。 メールの本文には、学生番号、氏名、科目名、授業日(10月24日)を明記してください。


5.4 参考文献


[小西ホームページ]   [目次・索引]   [前の授業]   [次の授業]

2008年10月24日更新
小西 善二郎 <konishi@cis.twcu.ac.jp>
Copyright (C) 2008 Zenjiro Konishi. All rights reserved.