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

コンピュータ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 ) とは、いくつかのデータをひとまとめにしたものです。 次の図はレコードのイメージを表しています。

An image of a 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 インスタンスの配列

これまで扱った配列は、要素が整数( 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.2  インスタンスの配列のイメージ
添字 フィールド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行目でそれを取り出します。

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 nargument 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%

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

次のプログラムでは、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:~/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 インスタンスの使用例

インスタンスの例として、次のような円の切断を描くアプレットを作成します。

Cuts of a circle
図 8.2  円の切断

円の切断は、円の中心 (100, 100) からどの位置まで直線を引くかで決まります。

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

Cut.java
/*  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)
};

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

CutMain.java
/*  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*/ }

8.2 演習8

次のような電球のパターンを描くアプレットを作成してください。

A pattern of lights
図 8.3  電球のパターン(LightMain)

この図は、スコアボードをイメージしています。 電球の直径は18とし、これが間隔20の格子状に配置されているとします。 したがって、電球がすべて点灯しますと、以下のようになります。

A location of lights
図 8.4  電球の配置

ファイル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)
};

この配列からパターンを描くアルゴリズムを考え、プログラムを作成してください。

余力のある人は、次のような電球のパターンを描いてください。

A pattern of lights
図 8.5  電球のパターン(LightMain2)

8.3 レポート課題

今日の演習8の答案(Javaプログラム)をメールで提出してください。 メールの送信には学内のコンピュータ(メール・サーバ)を用い、送信先はkonishi@twcu.ac.jpとします。 メールの本文には、学生番号、氏名、科目名、授業日(6月10日)を明記してください。


8.4 参考文献


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

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