目次 | 索引 |
---|---|
レコード ( 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:~/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
フィールドからデータを取り出して、出力します。
これまで扱った配列は、要素が整数(
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行目でそれを取り出します。
/* 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:~/java% java TimeArray 13:15 14:55 16:35 b04a001@AsiaA1:~/java%
クラスのインスタンスを扱う場合、これまでは、生成してからフィールドに値を格納しました。 例えば次のように書きました。
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:~/java% java ChimeTest 10:30 b04a001@AsiaA1:~/java%
配列の初期化が、ブレースの括弧内に要素を並べて書くとできることを思い出してください。 これとコンストラクタを組み合わせますと、インスタンスの配列の初期化が極めてコンパクトに書けます。
次のプログラムでは、4行目から6行目で、3つ時刻データからなるインスタンスの配列を
a
に格納しています。
上記の TimeArray.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; } }
インスタンスの例として、以下のような長方形をインスタンスの配列に格納し、その総面積を計算します。
幅 | 高さ | |
---|---|---|
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
というフィールドには高さを格納します。
コンストラクタは、引数が幅、高さの順になるように定義します。
/* 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)};
アルゴリズムは次のようになります。
Rectangle
の配列
rects
を宣言し、上記の要領で初期化する。
プログラムは以下のようになります。
/* 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%
次のような畳の配置を描いたアプレットを作成してください。
畳の大きさは、横 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)};
アルゴリズムは次のようにします。
Tatami
の配列
ttms
を宣言し、上記の要領で初期化する。
このアルゴリズムをプログラムにしてください。
余力のある人は、次の畳の配置を描いてください。
今日の演習8に従ってJavaプログラムを作成し、そのプログラムをkonishi@twcu.ac.jpあてにメールで提出してください。 メールには、学生番号、氏名、科目名、授業日(6/11)を明記してください。