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

情報処理IIIA(Javaプログラミング入門)第9回

目次 索引
9.1 レコードとしてのインスタンス
9.1.1 レコードとは
9.1.2 インスタンスの使い方
9.1.3 インスタンスの配列
9.1.4 コンストラクタ
9.1.5 インスタンスの使用例
9.2 演習9
9.3 レポート課題
インスタンス  コンストラクタ  フィールド  レコード 

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

9.1.1 レコードとは

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

An image of a record
図 9.1  レコードのイメージ

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

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

ここでレコード型とは、レコードを表すデータ型です。 レコード型の定義は、データ型としての名前、フィールドの名前、およびどのフィールドにどのデータ型が格納できるかの指定で成り立ちます。 ここで、フィールド hourminute に整数( int 型)が格納できるレコード型 Time を定義します。 上記のレコードは Time 型と考えることができます。

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

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

この授業では、インスタンス、フィールド、およびクラスという用語を用います。 ちなみに、プログラミング言語によっては、レコードのことを構造体とよびます。

9.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*/ }
b00a001@AsiaA1:~/java% java TimeTest
9:30
b00a001@AsiaA1:~/java%

変数に整数( 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 フィールドからデータを取り出して、出力します。

9.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

によってこの値が取り出せます。

次のプログラムは、配列に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*/ }
b00a001@AsiaA1:~/java% java TimeArray
13:15
14:55
16:35
b00a001@AsiaA1:~/java%

9.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*/ }
b00a001@AsiaA1:~/java% java ChimeTest
10:30
b00a001@AsiaA1:~/java%

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

次のプログラムでは、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*/ }
b00a001@AsiaA1:~/java% java ChimeArray
10:30
10:55
12:25
b00a001@AsiaA1:~/java%

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

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

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

9.1.5 インスタンスの使用例

インスタンスの例として、以下のような長方形をインスタンスの配列に格納し、その総面積を計算します。

表 9.2  長方形の大きさ
高さ
0 20 30
1 50 10
2 40 20
3 10 70
4 60 10

ファイル 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 では、次のようにコンストラクタを呼び出して、配列 rects にすべての幅と高さを格納します。

Rectangle[] rects = {new Rectangle(20, 30),
                     new Rectangle(50, 10),
                     new Rectangle(40, 20),
                     new Rectangle(10, 70),
                     new Rectangle(60, 10)};

アルゴリズムは次のようになります。

  1. ループ制御変数 i , 面積を表す変数 area , および総面積を表す変数 areaSum を宣言する。
  2. 変数 areaSum を 0 に初期化する。
  3. クラス Rectangle の配列 rects を宣言し、上記の要領で初期化する。
  4. 文字列"No.", "Width", "Height", および"Area"をタブで区切って出力する。
  5. 変数 i の値を 0 から長方形の個数未満の間増加させながら、以下を繰り返す。{
  6.      i 番目の長方形の幅と高さの積を変数 area に格納する。
  7.     変数 areaSum の値を area だけ増加させる。
  8.     変数 i の値とタブを改行なしで出力する。
  9.      i 番目の長方形の幅とタブを改行なしで出力する。
  10.      i 番目の長方形の高さとタブを改行なしで出力する。
  11.     変数 area の値を出力する。
  12. 文字列"Total"とタブ3つと変数 areaSum の値を出力する。

プログラムは以下のようになります。

RectangleMain.java
/*  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*/ }
b00a001@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
b00a001@AsiaA1:~/java%

なお、以前はこの計算を並列配列を用いて行いました。 プログラムを再掲しますので、今回のものと比較してみてください。

/*  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*/ }

9.2 演習9

次の5人の生年月日から小学校入学の年(西暦)を求めます。

表 9.3  生年月日
No.
0 2010 7 23
1 2011 11 1
2 2013 1 18
3 2014 4 1
4 2015 4 10

小学校は、4月1日現在の満年齢が6才の年に入学します。 したがって、早生まれの人は生まれ年に6を足し、そうでない人は7を足せば入学する年になります。 4月1日生まれは早生まれに含まれることに注意してください。

ファイルBirthDate.javaでは、生年月日を格納するために、クラス BirthDate を定義します。 フィールド year には年を、フィールド month には月を、フィールド day には日を格納します。 コンストラクタは、引数が年、月、日の順になるように定義します。

ファイルBirthDateMain.javaでは、次のようにコンストラクタを呼び出して、配列 children にすべての生年月日を格納します。

BirthDate[] children = {new BirthDate(2010, 7, 23),
                        new BirthDate(2011, 11, 1),
                        new BirthDate(2013, 1, 18),
                        new BirthDate(2014, 4, 1),
                        new BirthDate(2015, 4, 10)};

アルゴリズムは次のようにします。

  1. ループ制御変数 i と入学の年を表す変数 entrance を宣言する。
  2. クラス BirthDate の配列 children を宣言し、上記の要領で初期化する。
  3. 文字列"No.", "Birth date", および"Entrance year"をタブで区切って出力する。
  4. 変数 i の値を 0 から人数未満の間増加させながら、以下を繰り返す。{
  5.     もし i 番目の人の生まれ月が 4 未満か、4 と等しくて生まれ日が 1 と等しいならば、{
  6.          i 番目の人の生まれ年+6 を変数 entrance に格納する。
  7.     }そうでなければ、{
  8.          i 番目の人の生まれ年+7 を変数 entrance に格納する。
  9.     }
  10.     変数 i の値とタブを改行なしで出力する。
  11.      i 番目の人の生まれ年と文字列"/"を改行なしで出力する。
  12.      i 番目の人の生まれ月と文字列"/"を改行なしで出力する。
  13.      i 番目の人の生まれ日とタブを改行なしで出力する。
  14.     変数 entrance の値を出力する。

このアルゴリズムをプログラムにしてください。

b00a001@AsiaA1:~/java% java BirthDateMain
No.     Birth date      Entrance year
0       2010/7/23       2017
1       2011/11/1       2018
2       2013/1/18       2019
3       2014/4/1        2020
4       2015/4/10       2022
b00a001@AsiaA1:~/java%

余力のある人は、生年月日から入学する年を求めるメソッド entrance を定義して、それを呼び出してください。 メソッド entrance の引数は BirthDate child のみです。 このメソッドは整数を返しますので、 static int と指定します。

メソッド定義では、フィールド child . year , child . month , および child . day の値を使って入学する年を計算し、return文でそれを返します。 メソッド呼出しでは、入学する年として式 entrance( children [ i ]) の値を出力します。


9.3 レポート課題

今日の演習9に従ってJavaプログラムを作成し、そのプログラムをkonishi@twcu.ac.jpあてにメールで提出してください。 メールには、学生番号、氏名、科目名、授業日(11/20)を明記してください。


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

2003年11月20日更新
konishi@twcu.ac.jp
Copyright (C) 2003 Zenjiro Konishi. All rights reserved.