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

コンピュータ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.4 参考文献
索引
インスタンス   コンストラクタ   フィールド   レコード  

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

8.1.1 レコードとは

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

レコードのイメージ
図 8.1  レコードのイメージ

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

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

ここで、レコード型とは、レコードを表すデータ型です。 レコード型の定義は、データ型としての名前、フィールドの名前、およびどのフィールドにどのデータ型が格納できるかで構成されます。

これから、フィールド hourminute に整数( 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:~/comp2b% java TimeTest
9:30
b04a001@AsiaA1:~/comp2b%

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

classname variable;

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

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

variable = new classname();

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

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

classname variable = new classname();

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

variable.fieldname = expression;

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

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

variable.fieldname

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

8.1.3 インスタンスの配列

ここで、次のような表のデータを格納することを考えます。

表 8.2  表のデータ
番号 項目 x 項目 y
0 データ データ
1 データ データ
2 データ データ

このようなデータを格納するには、インスタンスの配列というものが適しています。

これまで扱った配列は、要素が整数( 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

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

まとめますと、インスタンスの配列の要素は、次のように表されます。

表 8.3  インスタンスの配列の要素
添字 フィールドx フィールドy
0 a[0].x a[0].y
1 a[1].x a[1].y
2 a[2].x a[2].y

以下のプログラムは、次の時刻データをインスタンスの配列に格納し、それを画面に出力するものです。

表 8.4  格納する時刻データ
番号
0 13 15
1 14 55
2 16 35

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:~/comp2b% java TimeArray
13:15
14:55
16:35
b04a001@AsiaA1:~/comp2b%

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

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

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

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

注意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.5  格納する点の座標
番号 x 座標 y 座標
0 120 50
1 130 60
2 140 70
3 150 60
4 160 50

ファイルPoint.javaでは、点の座標を格納するために、 Point というクラスを定義します。 x というフィールドには x 座標を、 y というフィールドには y 座標を格納します。 コンストラクタは、引数が、 x 座標、 y 座標の順になるように定義します。

Point.java
/*  1*/ class Point {
/*  2*/     int x, y;
/*  3*/     Point (int x, int y) {
/*  4*/         this.x = x;
/*  5*/         this.y = y;
/*  6*/     }
/*  7*/ }

ファイルPointMain.javaでは、次のようにコンストラクタを呼び出して、配列 points にすべての点の座標を格納します。

Point[] points = {
    new Point(120, 50),
    new Point(130, 60),
    new Point(140, 70),
    new Point(150, 60),
    new Point(160, 50)
};

プログラムは次の通りです。

PointMain.java
/*  1*/ class PointMain {
/*  2*/     public static void main (String[] args) {
/*  3*/         int i;
/*  4*/         Point[] points = {
/*  5*/             new Point(120, 50),
/*  6*/             new Point(130, 60),
/*  7*/             new Point(140, 70),
/*  8*/             new Point(150, 60),
/*  9*/             new Point(160, 50)
/* 10*/         };
/* 11*/         for (i = 0; i < points.length; i++) {
/* 12*/             System.out.print("(" + points[i].x + ", ");
/* 13*/             System.out.println(points[i].y + ")");
/* 14*/         }
/* 15*/     }
/* 16*/ }
b04a001@AsiaA1:~/comp2b% java PointMain
(120, 50)
(130, 60)
(140, 70)
(150, 60)
(160, 50)
b04a001@AsiaA1:~/comp2b%

次の例は、以下のような分数を格納して表示するプログラムです。

表 8.6  格納する分数
番号 分子 分母
0 1 2
1 1 3
2 2 3
3 1 4
4 3 4

ファイルFraction.javaでは、分数を格納するために、 Fraction というクラスを定義します。 numerator というフィールドには分子を、 denominator というフィールドには分母を格納します。 コンストラクタは、引数が、分子、分母の順になるように定義します。

Fraction.java
/*  1*/ class Fraction {
/*  2*/     int numerator, denominator;
/*  3*/     Fraction (int numerator, int denominator) {
/*  4*/         this.numerator = numerator;
/*  5*/         this.denominator = denominator;
/*  6*/     }
/*  7*/ }

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

Fraction[] fractions = {
    new Fraction(1, 2),
    new Fraction(1, 3),
    new Fraction(2, 3),
    new Fraction(1, 4),
    new Fraction(3, 4)
};

プログラムは次の通りです。

FractionMain.java
/*  1*/ class FractionMain {
/*  2*/     public static void main (String[] args) {
/*  3*/         int i;
/*  4*/         Fraction[] fractions = {
/*  5*/             new Fraction(1, 2),
/*  6*/             new Fraction(1, 3),
/*  7*/             new Fraction(2, 3),
/*  8*/             new Fraction(1, 4),
/*  9*/             new Fraction(3, 4)
/* 10*/         };
/* 11*/         for (i = 0; i < fractions.length; i++) {
/* 12*/             System.out.print(fractions[i].numerator + "/");
/* 13*/             System.out.println(fractions[i].denominator);
/* 14*/         }
/* 15*/     }
/* 16*/ }
b04a001@AsiaA1:~/comp2b% java FractionMain
1/2
1/3
2/3
1/4
3/4
b04a001@AsiaA1:~/comp2b%

8.2 演習8

ある新聞社が世論調査を行い、以下のような調査結果を得ました。 (簡単のために、その他の回答については除いています。) この調査結果を格納して表示するプログラムを作成してください。

表 8.7  世論調査の結果
質問番号 賛成の割合 反対の割合
0 36 59
1 13 86
2 64 29
3 34 66
4 11 87

ファイルQuestion.javaでは、調査結果を格納するために、クラス Question を定義します。 フィールド yes には賛成の割合を、フィールド no には反対の割合を格納します。 コンストラクタは、引数が、賛成、反対の順になるように定義します。

ファイルQuestionMain.javaでは、コンストラクタを呼び出して、配列 questions にすべての調査結果を格納します。 そして、 for 文を使って、格納されたすべての調査結果を、割合らしく出力します。

b04a001@AsiaA1:~/comp2b% java QuestionMain
36:59
13:86
64:29
34:66
11:87
b04a001@AsiaA1:~/comp2b%

余力のある人は、その他の割合についても格納できるようにクラス Question を変更し、次の調査結果を格納して出力するようにしてください。 その他の割合のフィールドは、 other とします。

表 8.8  世論調査の結果
質問番号 賛成の割合 反対の割合 その他の割合
0 36 59 5
1 13 86 1
2 64 29 7
3 34 66 0
4 11 87 2
b04a001@AsiaA1:~/comp2b% java QuestionMain
36:59:5
13:86:1
64:29:7
34:66:0
11:87:2
b04a001@AsiaA1:~/comp2b%

8.3 レポート課題

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


8.4 参考文献


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

2007年6月15日更新
小西 善二郎 <konishi@twcu.ac.jp>
Copyright (C) 2007 Zenjiro Konishi. All rights reserved.