目次 | 索引 |
---|---|
レコード ( 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
フィールドからデータを取り出して、出力します。
これまで扱った配列は、要素が整数(
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 |
次のプログラムは、配列に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:~/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%
配列の初期化が、ブレースの括弧内に要素を並べて書くとできることを思い出してください。 これとコンストラクタを組み合わせますと、インスタンスの配列の初期化が極めてコンパクトに書けます。
次のプログラムでは、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:~/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; } }
インスタンスの例として、次のような円の切断を描くアプレットを作成します。
円の切断は、円の中心 (100, 100) からどの位置まで直線を引くかで決まります。
ファイルCut.javaでは、切断の位置を格納するために、
Cut
というクラスを定義します。
posX
というフィールドには
x
座標を、
posY
というフィールドには
y
座標を格納します。
コンストラクタは、引数が、
x
座標、
y
座標の順になるように定義します。
/* 1*/ class Cut { /* 2*/ int posX, posY; /* 3*/ Cut (int posX, int posY) { /* 4*/ this.posX = posX; /* 5*/ this.posY = posY; /* 6*/ } /* 7*/ }
ファイルCutMain.javaでは、次のようにコンストラクタを呼び出して、配列 cuts にすべての切断の位置を格納します。
Cut[] cuts = { new Cut(100, 20), new Cut(24, 75), new Cut(53, 165), new Cut(147, 165), new Cut(176, 75) };
プログラムは次の通りです。
/* 1*/ import java.applet.*; /* 2*/ import java.awt.*; /* 3*/ /* 4*/ public class CutMain extends Applet { /* 5*/ public void paint (Graphics g) { /* 6*/ int i; /* 7*/ Cut[] cuts = { /* 8*/ new Cut(100, 20), /* 9*/ new Cut(24, 75), /* 10*/ new Cut(53, 165), /* 11*/ new Cut(147, 165), /* 12*/ new Cut(176, 75) /* 13*/ }; /* 14*/ g.drawOval(20, 20, 160, 160); /* 15*/ for (i = 0; i < cuts.length; i++) { /* 16*/ g.drawLine(cuts[i].posX, cuts[i].posY, 100, 100); /* 17*/ } /* 18*/ } /* 19*/ }
次のような電球のパターンを描くアプレットを作成してください。
この図は、スコアボードをイメージしています。 電球の直径は18とし、これが間隔20の格子状に配置されているとします。 したがって、電球がすべて点灯しますと、以下のようになります。
ファイルLight.javaでは、電球の位置を格納するために、クラス
Light
を定義します。
フィールド
posX
には左から何番目かを、フィールド
posY
には上から何番目かを格納します。
(0番目から数え始めます。)
コンストラクタは、引数が、左から何番目か、上から何番目か、の順になるように定義します。
ファイルLightMain.javaでは、次のようにコンストラクタを呼び出して、配列 lights にすべての電球の位置を格納します。
Light[] lights = { new Light(3, 1), new Light(4, 1), new Light(5, 1), new Light(2, 2), new Light(6, 2), new Light(2, 3), new Light(6, 3), new Light(2, 4), new Light(6, 4), new Light(2, 5), new Light(6, 5), new Light(2, 6), new Light(6, 6), new Light(3, 7), new Light(4, 7), new Light(5, 7) };
この配列からパターンを描くアルゴリズムを考え、プログラムを作成してください。
余力のある人は、次のような電球のパターンを描いてください。
今日の演習8の答案(Javaプログラム)をメールで提出してください。 メールの送信には学内のコンピュータ(メール・サーバ)を用い、送信先はkonishi@twcu.ac.jpとします。 メールの本文には、学生番号、氏名、科目名、授業日(6月10日)を明記してください。