レコード ( record ) とは、いくつかのデータをひとまとめにしたものです。 次の図はレコードのイメージを表しています。
レコードの構成要素を フィールド ( field ) とよびます。
データをまとめたものとして、以前に配列を取り上げました。 レコードと配列には次のような違いがあります。
int[]
などと規則的な名前がつきますが、レコード型には個別に名前をつけます。
ここで、レコード型とは、レコードを表すデータ型です。 レコード型の定義は、データ型としての名前、フィールドの名前、およびどのフィールドにどのデータ型が格納できるかで構成されます。
これから、フィールド
hour
と
minute
に整数(
int
型)が格納できるレコード型
Time
を定義します。
上記のレコードは、
Time
型のデータと言えます。
実は、Javaにはレコードはありません。 しかし、 インスタンス ( instance ) というものがレコードの機能を持っています。 レコードとインスタンスの対応は次の通りです。
一般 | Java |
---|---|
レコード | インスタンス |
フィールド | インスタンス変数 |
レコード型 | クラス |
この授業では、インスタンス、フィールド、およびクラスという用語を用います。 ちなみに、プログラミング言語によっては、レコードのことを構造体とよびます。
レコードとしてのインスタンスを使うには、まずクラスを定義する必要があります。
フィールドに格納するデータが整数(
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行目で、変数
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 フィールドからデータを取り出して、出力します。
ここで、次のような表のデータを格納することを考えます。
番号 | 項目 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
によってこの値が取り出せます。
まとめますと、インスタンスの配列の要素は、次のように表されます。
添字 | フィールドx | フィールドy |
---|---|---|
0 | a[0].x | a[0].y |
1 | a[1].x | a[1].y |
2 | a[2].x | a[2].y |
以下のプログラムは、次の時刻データをインスタンスの配列に格納し、それを画面に出力するものです。
番号 | 時 | 分 |
---|---|---|
0 | 13 | 15 |
1 | 14 | 55 |
2 | 16 | 35 |
4行目で、
Time
クラスの配列の変数
a
を宣言し、配列を生成して
a
に格納します。
5行目から7行目までで、インスタンスを生成して配列の各要素
a
[
i
] に格納します。
これで、インスタンスのフィールドに整数が格納できます。
8行目から10行目までで、フィールドに整数を格納し、12行目でそれを取り出します。
/* 1*/ class Time { /* 2*/ int hour, minute; /* 3*/ }
/* 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%
クラスのインスタンスを扱う場合、これまでは、生成してからフィールドに値を格納しました。 例えば次のように書きました。
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 を格納したインスタンスになります。
コンストラクタはメソッドに似ています。 どちらも、あらかじめ定義しておき、呼出しによってその機能が働きます。 コンストラクタとメソッドは以下の点が異なります。
void
や
int
などと書いて、返り値の型を指定しました。
コンストラクタ定義では、返り値の型は指定しません。
new
を用います。
次のプログラムでは、チャイムの時刻を表すクラス
Chime
を定義しています。
Chime.java の3行目から6行目までが、コンストラクタ定義です。
このコンストラクタは、第1引数を
hour
フィールドに格納し、第2引数を
minute
フィールドに格納します。
ChimeTest.java の3行目で、このコンストラクタが呼び出され、
hour
フィールドに10が格納され、
minute
フィールドに30が格納されます。
/* 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*/ }
b04a001@AsiaA1:~/comp2b% java ChimeTest 10:30 b04a001@AsiaA1:~/comp2b%
配列の初期化が、括弧内 {...} に要素を並べて書くことを思い出してください。 これとコンストラクタを組み合わせますと、インスタンスの配列の初期化が極めてコンパクトに書けます。
次のプログラムでは、5行目から7行目で、3つ時刻データからなるインスタンスの配列を a に格納しています。 上記の TimeArray.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; } }
インスタンスの例として、次のような点の座標を格納して表示するプログラムを作成します。
番号 | 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
座標の順になるように定義します。
/* 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) };
プログラムは次の通りです。
/* 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%
次の例は、以下のような分数を格納して表示するプログラムです。
番号 | 分子 | 分母 |
---|---|---|
0 | 1 | 2 |
1 | 1 | 3 |
2 | 2 | 3 |
3 | 1 | 4 |
4 | 3 | 4 |
ファイルFraction.javaでは、分数を格納するために、
Fraction
というクラスを定義します。
numerator
というフィールドには分子を、
denominator
というフィールドには分母を格納します。
コンストラクタは、引数が、分子、分母の順になるように定義します。
/* 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) };
プログラムは次の通りです。
/* 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%
ある新聞社が世論調査を行い、以下のような調査結果を得ました。 (簡単のために、その他の回答については除いています。) この調査結果を格納して表示するプログラムを作成してください。
質問番号 | 賛成の割合 | 反対の割合 |
---|---|---|
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
とします。
質問番号 | 賛成の割合 | 反対の割合 | その他の割合 |
---|---|---|---|
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の答案(Javaプログラム)をメールで提出してください。 差出人は学内のメール・アドレス(b04a001@twcu.ac.jpなど)とし、宛先はkonishi@twcu.ac.jpとします。 メールの本文には、学生番号、氏名、科目名、授業日(6月15日)を明記してください。