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

コンピュータIIB(Javaプログラミング入門)第10回

目次
10.1 レコードとしてのインスタンス
10.1.1 レコードとは
10.1.2 インスタンスの使い方
10.1.3 コンストラクタ
10.1.4 インスタンスの引数
10.1.5 インスタンスの使用例
10.2 演習10
10.3 レポート課題
10.4 参考文献
索引
インスタンス   コンストラクタ   フィールド   レコード  

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

10.1.1 レコードとは

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

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

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

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

ここで、レコード型とは、レコードを表すデータ型です。

レコード型は、データ型としての名前、フィールドの名前、およびそれぞれのフィールドのデータ型で定義されます。 上記のイメージの場合は、データ型としての名前をTime, フィールドの名前を hourminute , フィールドのデータ型を整数型( int 型)と定義します。

実は、Javaにはレコードはありません。 しかし、 インスタンスinstance ) というものがレコードの機能を持っています。 レコードとインスタンスの対応は次の通りです。

表 10.1  レコードとインスタンスの対応
一般 Java
レコード インスタンス
フィールド インスタンス変数
レコード型 クラス

この授業では、インスタンス、フィールド、およびクラスという用語を用います。

ちなみに、プログラミング言語によっては、レコードのことを構造体とよびます。

10.1.2 インスタンスの使い方

レコードとしてのインスタンスを使うには、まず、クラスを定義します。 フィールドのデータ型が整数型( int 型)なら、クラスの定義は次のようになります。

class classname {
    int fieldname1, ..., fieldnamen;
}

ここで、 classname はクラス名、 fieldname はフィールド名です。 上記の例なら、次の通りです。

/*  1*/ class Time { // クラス名
/*  2*/     int hour, minute; // フィールド名
/*  3*/ }

これをファイルTime.javaに保存します。 このファイルがないと、以下のプログラムは動きません。

クラスが定義できたら、プログラムを作成します。 次のプログラムは、変数を宣言し、インスタンスを生成してそこに格納し、インスタンスのフィールドにデータを格納し、その値を表示するものです。

/*  1*/ class TimeTest { // Timeクラスのテスト
/*  2*/     public static void main (String[] args) {
/*  3*/         Time x; // 宣言
/*  4*/         x = new Time(); // 生成
/*  5*/         x.hour = 9; // hourフィールドに格納
/*  6*/         x.minute = 30; // minuteフィールドに格納
/*  7*/         System.out.println(x.hour + ":" + x.minute); // フィールドの値を出力
/*  8*/     }
/*  9*/ }
asiaa1:~/comp2b b08a001$ java TimeTest
9:30
asiaa1:~/comp2b b08a001$

変数に整数( 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行目で、インスタンス xhour フィールドに9が格納され、6行目で、 minute フィールドに30が格納されます。

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

variable.fieldname

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

10.1.3 コンストラクタ

クラスのインスタンスを扱う場合、これまでは、インスタンスを生成してからフィールドに値を格納しました。 例えば次のように書きました。

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 { // Chimeクラスのテスト
/*  2*/     public static void main (String[] args) {
/*  3*/         Chime c = new Chime(10, 30); // コンストラクタ呼出し
/*  4*/         System.out.println(c.hour + ":" + c.minute);
/*  5*/     }
/*  6*/ }
asiaa1:~/comp2b b08a001$ java ChimeTest
10:30
asiaa1:~/comp2b b08a001$

注意1. 上記のコンストラクタは、「典型的なコンストラクタ」と呼ぶべきものです。 一般的には、コンストラクタはより複雑な機能を持ちます。

注意2. 上記のようにコンストラクタを定義すると、 new Chime() ではインスタンスが生成できなくなります。 実は、コンストラクタを定義しなければ引数なしのコンストラクタが自動的に用意され、コンストラクタを定義すると定義したものだけが使えるという規則になっています。 引数ありと引数なしの両方のコンストラクタを使うには、次のように両方定義します。

class Chime {
    int hour, minute;
    Chime () {
    }
    Chime (int hour, int minute) {
        this.hour = hour;
        this.minute = minute;
    }
}

10.1.4 インスタンスの引数

前回、引数が配列であるメソッドを定義しました。 引数がインスタンスであるメソッドも、同じように定義できます。 これによって、インスタンスに関する処理にも名前を付けることができます。

引数がインスタンスであるメソッドを定義するには、整数のときの int の代わりに、クラス名を書きます。 値を返さないメソッドならば、

static void methodname (classname argument, ...) {
    statement; ...
}

と書き、整数( int 型)を返すメソッドならば、

static int methodname (classname argument, ...) {
    statement; ...
}

と書きます。

メソッドを呼び出すには、引数にインスタンスを表す変数名を書きます。 値を返さないメソッドを呼び出すには、文として

methodname(argument, ...);

と書きます。 値を返すメソッドを呼び出すには、式として

methodname(argument, ...)

と書きます。

ここで、例として、1:30 = 90(1時間30分は90分間)という計算をするプログラムを考えます。

ファイルPeriod.javaでは、期間を格納するために、Periodというクラスを定義します。 hour というフィールドには時間を、 minute というフィールドには分を格納します。 コンストラクタは、引数が時間、分の順になるように定義します。

Period.java
/*  1*/ class Period { // 期間
/*  2*/     int hour, minute;
/*  3*/     Period (int hour, int minute) {
/*  4*/         this.hour = hour;
/*  5*/         this.minute = minute;
/*  6*/     }
/*  7*/ }

メソッドを使わないプログラムは以下の通りです。

/*  1*/ class PeriodTest { // Periodクラスのテスト
/*  2*/     public static void main (String[] args) {
/*  3*/         Period period = new Period(1, 30); // 期間は1時間30分
/*  4*/         System.out.print(period.hour + ":" + period.minute); // 期間を出力
/*  5*/         System.out.println(" = " + (period.hour * 60 + period.minute)); // 通算の分を計算
/*  6*/     }
/*  7*/ }
asiaa1:~/comp2b b08a001$ java PeriodTest
1:30 = 90
asiaa1:~/comp2b b08a001$

このプログラムを、メソッドを使って書き直します。

期間を出力するメソッドの名前は printPeriod とします。 このメソッドの引数は、Periodクラスの p です。 このメソッドは値を返さないので、 static void と指定します。

通算の分を計算するメソッドの名前は getTotal とします。 このメソッドの引数は、Periodクラスの p です。 このメソッドは整数( int 型)を返すので、 static int と指定します。

/*  1*/ class PeriodTest2 { // Periodクラスのテスト2
/*  2*/     public static void main (String[] args) {
/*  3*/         Period period = new Period(1, 30);
/*  4*/         printPeriod(period);
/*  5*/         System.out.println(" = " + getTotal(period));
/*  6*/     }
/*  7*/     static void printPeriod (Period p) {
/*  8*/         System.out.print(p.hour + ":" + p.minute);
/*  9*/     }
/* 10*/     static int getTotal (Period p) {
/* 11*/         return p.hour * 60 + p.minute;
/* 12*/     }
/* 13*/ }

10.1.5 インスタンスの使用例

インスタンスの例として、次のように長方形を格納して面積を計算するプログラムを作成します。

Rectangle rect1 = new Rectangle(10, 50);
Rectangle rect2 = new Rectangle(20, 40);
Rectangle rect3 = new Rectangle(30, 30);

ファイルRectangle.javaでは、長方形を格納するために、Rectangleというクラスを定義します。 width というフィールドには幅を、 height というフィールドには高さを格納します。 コンストラクタは、引数が幅、高さの順になるように定義します。

Rectangle.java
/*  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では、メソッドを定義します。

長方形の大きさを出力するメソッドの名前は printRectangle とします。 このメソッドの引数は、Rectangleクラスの r です。 このメソッドは値を返しませんので、 static void と指定します。

面積を計算するメソッドの名前は getArea とします。 このメソッドの引数は、Rectangleクラスの r です。 このメソッドは整数( int 型)を返すので、 static int と指定します。

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

RectangleMain.java
/*  1*/ class RectangleMain { // 長方形のメイン
/*  2*/     public static void main (String[] args) {
/*  3*/         Rectangle rect1 = new Rectangle(10, 50);
/*  4*/         Rectangle rect2 = new Rectangle(20, 40);
/*  5*/         Rectangle rect3 = new Rectangle(30, 30);
/*  6*/         printRectangle(rect1);
/*  7*/         System.out.println("の面積は" + getArea(rect1));
/*  8*/         printRectangle(rect2);
/*  9*/         System.out.println("の面積は" + getArea(rect2));
/* 10*/         printRectangle(rect3);
/* 11*/         System.out.println("の面積は" + getArea(rect3));
/* 12*/     }
/* 13*/     static void printRectangle (Rectangle r) {
/* 14*/         System.out.print("幅" + r.width + "高さ" + r.height);
/* 15*/     }
/* 16*/     static int getArea (Rectangle r) {
/* 17*/         return r.width * r.height;
/* 18*/     }
/* 19*/ }
asiaa1:~/comp2b b08a001$ java RectangleMain
幅10高さ50の面積は500
幅20高さ40の面積は800
幅30高さ30の面積は900
asiaa1:~/comp2b b08a001$

10.2 演習10

ある小学校で国語と算数の試験を行ったと想定し、次の出力が得られるようなプログラムを作成してください。

asiaa1:~/comp2b b08a001$ java ExaminationMain
国語80算数70の合計は150
国語90算数80の合計は170
国語100算数90の合計は190
asiaa1:~/comp2b b08a001$

試験の得点は次のように格納します。

Examination exam1 = new Examination(80, 70);
Examination exam2 = new Examination(90, 80);
Examination exam3 = new Examination(100, 90);

ファイルExamination.javaでは、試験の得点を格納するために、クラスExaminationを定義します。 フィールド japanese には国語の得点を、フィールド arithmetic には算数の得点を格納します。 コンストラクタは、引数が国語、算数の順になるように定義します。

ファイルExaminationMain.javaでは、メソッドを定義します

試験の得点を出力するメソッドの名前は printExamination とします。 このメソッドの引数は、Examinationクラスの e です。 このメソッドは値を返さないので、 static void と指定します。

合計を計算するメソッドの名前は getTotal とします。 このメソッドの引数は、Examinationクラスの e です。 このメソッドは整数( int 型)を返すので、 static int と指定します。

余力のある人は、平均を計算するメソッド getAverage も定義し、それを呼び出して、出力してください。

asiaa1:~/comp2b b08a001$ java ExaminationMain2
国語80算数70の合計は150で平均は75
国語90算数80の合計は170で平均は85
国語100算数90の合計は190で平均は95
asiaa1:~/comp2b b08a001$

10.3 レポート課題

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


10.4 参考文献


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

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