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

コンピュータIIB(Javaプログラミング入門)第8回

目次 索引
8.1 レコードとしてのインスタンス
8.1.1 レコードとは
8.1.2 インスタンスの使い方
8.1.3 インスタンスの配列
8.1.4 コンストラクタ
8.1.5 インスタンスの使用例
8.2 演習8
8.3 レポート課題
インスタンス  コンストラクタ  フィールド  レコード 

8.1 レコードとしてのインスタンス

8.1.1 レコードとは

レコード ( record ) とは、いくつかのデータをひとまとめにしたものです。 次の図はレコードのイメージを表しています。

An image of a record
図 8.1  レコードのイメージ

レコードの構成要素を フィールド ( field ) とよびます。

データをまとめたものとして、以前に配列を取り上げました。 レコードと配列には次のような違いがあります。

ここでレコード型とは、レコードを表すデータ型です。 レコード型の定義は、データ型としての名前、フィールドの名前、およびどのフィールドにどのデータ型が格納できるかの指定で成り立ちます。 ここで、フィールド hour と minute に整数( int 型)が格納できるレコード型 Time を定義します。 上記のレコードは Time 型と考えることができます。

実は、Javaにはレコードはありません。 しかし、 インスタンス ( instance ) というものがレコードの機能を持っています。 レコードとインスタンスの対応は次の通りです。

表 8.1  レコードとインスタンスの対応
一般 Java
レコード インスタンス
フィールド インスタンス変数
レコード型 クラス

この授業では、インスタンス、フィールド、およびクラスという用語を用います。 ちなみに、プログラミング言語によっては、レコードのことを構造体とよびます。

8.1.2 インスタンスの使い方

レコードとしてのインスタンスを使うには、まずクラスを定義する必要があります。 フィールドに格納するデータが整数( int 型)でしたら、次のように書きます。

class classname {
    int fieldname1, ..., fieldnamen;
}

ここで、 classname はクラス名、 fieldname はフィールド名です。 上記の例ですと、次のようになります。

/*  1*/ class Time {
/*  2*/     int hour, minute;
/*  3*/ }

これをファイル(Time.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*/ }
b04a001@AsiaA1:~/java% java TimeTest
9:30
b04a001@AsiaA1:~/java%

変数に整数( int 型)を格納するには変数の宣言が必要でした。 変数にインスタンスを格納するにも、同様に変数の宣言が必要です。 これは次のように書きます。

classname variable;

今まで int と書いていた部分をクラス名に変えます。 3行目で、変数 x を Time クラスと宣言します。

クラスのインスタンスは、明示的に生成してはじめて使えます。 インスタンスを生成するにはキーワード new を用います。 クラス classname のインスタンスを生成して、変数 variable に格納するには、

variable = new classname();

と書きます。 4行目で、 Time クラスのインスタンスを生成して、変数 x に格納します。

なお、次のように書きますと、変数の宣言とインスタンスの生成、そしてその変数への格納が同時に行えます。

classname variable = new classname();

インスタンスのフィールドにデータを格納するには、次のような文を用います。

variable.fieldname = expression;

これで、変数 variable に格納されているインスタンスのフィールド fieldname に、式 expression の値が格納されます。 5行目で、インスタンス x の hour フィールドに9が格納され、6行目で、 minute フィールドに30が格納されます。

インスタンスのフィールドに格納されたデータを取り出すには、式

variable.fieldname

を用います。 この式の値は、変数 variable に格納されているインスタンスのフィールド fieldname に格納されているデータです。 7行目で、インスタンス x の hour フィールドと minute フィールドからデータを取り出して、出力します。

8.1.3 インスタンスの配列

これまで扱った配列は、要素が整数( int 型)のものだけでした。 以下のようにしますと、要素がクラスのインスタンスである配列が使えます。 要点は、これまで int と書いていた部分に、クラス名 classname を書くことです。

まず、変数の宣言は

classname[] arrayname;

と書きます。 大きさが size である配列を生成して変数 arrayname に格納するには次のようにします。

arrayname = new classname[size];

このふたつは次のようにまとめられます。

classname[] arrayname = new classname[size];

この段階では、まだ配列の要素には何も格納されていません。 インスタンスを生成して、配列の各要素に格納する必要があります。 これは、例えば次のようにします。

int i;
...
for (i = 0; i < arrayname.length; i++) {
    arrayname[i] = new classname();
}

配列の要素に格納されたインスタンスのフィールドへの代入は、文

arrayname[index].fieldname = expression;

によって行います。 式

arrayname[index].fieldname

によってこの値が取り出せます。

次のプログラムは、配列に3つの時刻データを格納し、それを画面に出力するものです。 4行目で、 Time クラスの配列の変数 a を宣言し、配列を生成して a に格納します。 5行目から7行目までで、インスタンスを生成して配列の各要素 a[i] に格納します。 これで、インスタンスのフィールドに整数が格納できます。 8行目から10行目までで、フィールドに整数を格納し、12行目でそれを取り出します。

Time.java
/*  1*/ class Time {
/*  2*/     int hour, minute;
/*  3*/ }
TimeArray.java
/*  1*/ class TimeArray {
/*  2*/     public static void main (String[] args) {
/*  3*/         int i;
/*  4*/         Time[] a = new Time[3];
/*  5*/         for (i = 0; i < a.length; i++) {
/*  6*/             a[i] = new Time();
/*  7*/         }
/*  8*/         a[0].hour = 13; a[0].minute = 15;
/*  9*/         a[1].hour = 14; a[1].minute = 55;
/* 10*/         a[2].hour = 16; a[2].minute = 35;
/* 11*/         for (i = 0; i < a.length; i++) {
/* 12*/             System.out.println(a[i].hour + ":" + a[i].minute);
/* 13*/         }
/* 14*/     }
/* 15*/ }
b04a001@AsiaA1:~/java% java TimeArray
13:15
14:55
16:35
b04a001@AsiaA1:~/java%

8.1.4 コンストラクタ

クラスのインスタンスを扱う場合、これまでは、生成してからフィールドに値を格納しました。 例えば次のように書きました。

Time x = new Time();
x.hour = 13;
x.minute = 15;

変数や配列では、初期化を使ってプログラムを短くしました。 インスタンスでも、 コンストラクタ ( constructor ) というものを用いますと、初期化に相当することができます。 上記の例ですと、例えば次のように書けます。

Time x = new Time(13, 15);

もしクラスが

class classname {
    int fieldname1, ..., fieldnamen;
}

と定義されているのでしたら、次のように定義し直しますと、コンストラクタが使えるようになります。

class classname {
    int fieldname1, ..., fieldnamen;
    classname (int fieldname1, ..., int fieldnamen) {
        this.fieldname1 = fieldname1;
        ...
        this.fieldnamen = fieldnamen;
    }
}

これで、式

new classname(argument1, ..., argumentn)

を呼び出しますと、フィールド fieldname 1 に値 argument 1 を格納し、…、 fieldname n に argument n を格納したインスタンスになります。

コンストラクタはメソッドに似ています。 どちらも、あらかじめ定義しておき、呼出しによってその機能が働きます。 コンストラクタとメソッドは以下の点が異なります。

次のプログラムでは、チャイムの時刻を表すクラス Chime を定義しています。 Chime.java の3行目から6行目までが、コンストラクタ定義です。 このコンストラクタは、第1引数を hour フィールドに格納し、第2引数を minute フィールドに格納します。 ChimeTest.java の3行目で、このコンストラクタが呼び出され、 hour フィールドに10が格納され、 minute フィールドに30が格納されます。

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*/ }
b04a001@AsiaA1:~/java% java ChimeTest
10:30
b04a001@AsiaA1:~/java%

配列の初期化が、ブレースの括弧内に要素を並べて書くとできることを思い出してください。 これとコンストラクタを組み合わせますと、インスタンスの配列の初期化が極めてコンパクトに書けます。

次のプログラムでは、4行目から6行目で、3つ時刻データからなるインスタンスの配列を a に格納しています。 上記の TimeArray.java と比べてみてください。

ChimeArray.java
/*  1*/ class ChimeArray {
/*  2*/     public static void main (String[] args) {
/*  3*/         int i;
/*  4*/         Chime[] a = {new Chime(10, 30),
/*  5*/                      new Chime(10, 55),
/*  6*/                      new Chime(12, 25)};
/*  7*/         for (i = 0; i < a.length; i++) {
/*  8*/             System.out.println(a[i].hour + ":" + a[i].minute);
/*  9*/         }
/* 10*/     }
/* 11*/ }
b04a001@AsiaA1:~/java% java ChimeArray
10:30
10:55
12:25
b04a001@AsiaA1:~/java%

注意1. 上記のコンストラクタは、「典型的なコンストラクタ」と呼ぶべきものです。 一般的には、コンストラクタはより複雑な機能を持ちます。

注意2. 上記のようにコンストラクタを定義しますと、 new Chime() ではインスタンスが生成できなくなります。 実は、これは引数なしのコンストラクタであり、明示的にコンストラクタが定義されないときに限り、自動的に用意されるものです。 引数ありのコンストラクタと引数なしのコンストラクタを両方使うには、次のようにします。

class Chime {
    int hour, minute;
    Chime () {
    }
    Chime (int hour, int minute) {
        this.hour = hour;
        this.minute = minute;
    }
}

8.1.5 インスタンスの使用例

インスタンスの例として、以下のような長方形をインスタンスの配列に格納し、その総面積を計算します。

表 8.2  長方形の大きさ
幅 高さ
0 20 30
1 50 10
2 40 20
3 10 70
4 60 10

以前は、この計算を並列配列を用いて次のように行いました。

/*  1*/ // 円記号対策のコメント
/*  2*/ class RectangleArea {
/*  3*/     public static void main (String[] args) {
/*  4*/         int i, area, areaSum = 0;
/*  5*/         int[] width = {20, 50, 40, 10, 60};
/*  6*/         int[] height = {30, 10, 20, 70, 10};
/*  7*/         System.out.println("No.\tWidth\tHeight\tArea");
/*  8*/         for (i = 0; i < width.length; i++) {
/*  9*/             area = width[i] * height[i];
/* 10*/             areaSum = areaSum + area;
/* 11*/             System.out.print(i + "\t");
/* 12*/             System.out.print(width[i] + "\t");
/* 13*/             System.out.print(height[i] + "\t");
/* 14*/             System.out.println(area);
/* 15*/         }
/* 16*/         System.out.println("Total\t\t\t" + areaSum);
/* 17*/     }
/* 18*/ }

今回は、並列配列ではなくインスタンスの配列を用います。

ファイル Rectangle.java では、幅と高さを格納するために、 Rectangle というクラスを定義します。 width というフィールドには幅を、 height というフィールドには高さを格納します。 コンストラクタは、引数が幅、高さの順になるように定義します。

Rectangle.java
/*  1*/ class Rectangle {
/*  2*/     int width, height;
/*  3*/     Rectangle (int width, int height) {
/*  4*/         this.width = width;
/*  5*/         this.height = height;
/*  6*/     }
/*  7*/ }

ファイル RectangleMain.java では、次のようにコンストラクタを呼び出して、配列 rects にすべての幅と高さを格納します。

Rectangle[] rects = {new Rectangle(20, 30),
                     new Rectangle(50, 10),
                     new Rectangle(40, 20),
                     new Rectangle(10, 70),
                     new Rectangle(60, 10)};

アルゴリズムは次のようになります。

  1. ループ制御変数 i , 面積を表す変数 area , および総面積を表す変数 areaSum を宣言する。
  2. 変数 areaSum を 0 に初期化する。
  3. クラス Rectangle の配列 rects を宣言し、上記の要領で初期化する。
  4. 文字列"No.", "Width", "Height", および"Area"をタブで区切って出力する。
  5. 変数 i の値を 0 から長方形の個数未満の間増加させながら、以下を繰り返す。{
  6.      i 番目の長方形の幅と高さの積を変数 area に格納する。
  7.     変数 areaSum の値を area だけ増加させる。
  8.     変数 i の値とタブを改行なしで出力する。
  9.      i 番目の長方形の幅とタブを改行なしで出力する。
  10.      i 番目の長方形の高さとタブを改行なしで出力する。
  11.     変数 area の値を出力する。
  12. }
  13. 文字列"Total"とタブ3つと変数 areaSum の値を出力する。

プログラムは以下のようになります。

RectangleMain.java
/*  1*/ // 円記号対策のコメント
/*  2*/ class RectangleMain {
/*  3*/     public static void main (String[] args) {
/*  4*/         int i, area, areaSum = 0;
/*  5*/         Rectangle[] rects = {new Rectangle(20, 30),
/*  6*/                              new Rectangle(50, 10),
/*  7*/                              new Rectangle(40, 20),
/*  8*/                              new Rectangle(10, 70),
/*  9*/                              new Rectangle(60, 10)};
/* 10*/         System.out.println("No.\tWidth\tHeight\tArea");
/* 11*/         for (i = 0; i < rects.length; i++) {
/* 12*/             area = rects[i].width * rects[i].height;
/* 13*/             areaSum = areaSum + area;
/* 14*/             System.out.print(i + "\t");
/* 15*/             System.out.print(rects[i].width + "\t");
/* 16*/             System.out.print(rects[i].height + "\t");
/* 17*/             System.out.println(area);
/* 18*/         }
/* 19*/         System.out.println("Total\t\t\t" + areaSum);
/* 20*/     }
/* 21*/ }
b04a001@AsiaA1:~/java% java RectangleMain
No.     Width   Height  Area
0       20      30      600
1       50      10      500
2       40      20      800
3       10      70      700
4       60      10      600
Total                   3200
b04a001@AsiaA1:~/java%

8.2 演習8

次のような畳の配置を描いたアプレットを作成してください。

A pattern of tatami mats
図 8.2  畳の配置(TatamiMain)

畳の大きさは、横 80 弱、縦 40 弱とします。 一つの畳は、左上の座標と、横長に置くか縦長に置くかで配置が決まります。

ファイルTatami.javaでは、畳の配置を格納するために、クラス Tatami を定義します。 フィールド posX には左上の x 座標を、フィールド posY には左上の y 座標を、フィールド wide には、横長に置くならば 1, 縦長に置くならば 0 を格納します。 コンストラクタは、引数が x 座標、 y 座標、横長か?の順になるように定義します。

ファイルTatamiMain.javaでは、次のようにコンストラクタを呼び出して、配列 ttms にすべての畳の配置を格納します。

Tatami[] ttms = {new Tatami(20, 20, 1),
                 new Tatami(100, 20, 1),
                 new Tatami(20, 60, 0),
                 new Tatami(60, 60, 1),
                 new Tatami(140, 60, 0),
                 new Tatami(60, 100, 1)};

アルゴリズムは次のようにします。

  1. ループ制御変数 i を宣言する。
  2. クラス Tatami の配列 ttms を宣言し、上記の要領で初期化する。
  3. 変数 i の値を 0 から畳の枚数未満の間増加させながら、以下を繰り返す。{
  4.     もし i 番目の畳を横長に置くならば、{
  5.          i 番目の畳の座標に、幅 77 高さ 37 の長方形を描く。
  6.     }そうでないならば、{
  7.          i 番目の畳の座標に、幅 37 高さ 77 の長方形を描く。
  8.     }
  9. }

このアルゴリズムをプログラムにしてください。

余力のある人は、次の畳の配置を描いてください。

A pattern of tatami mats
図 8.3  畳の配置(TatamiMain2)

8.3 レポート課題

今日の演習8に従ってJavaプログラムを作成し、そのプログラムをkonishi@twcu.ac.jpあてにメールで提出してください。 メールには、学生番号、氏名、科目名、授業日(6/11)を明記してください。


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

2004年6月11日更新
konishi@twcu.ac.jp
Copyright (C) 2004 Zenjiro Konishi. All rights reserved.