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

情報処理技法(Javaプログラミング)I 第13回

目次
索引

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

レコードとは

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

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

レコードの構成要素を フィールド field )とよびます。 また、レコード自身のデータ型を レコード型 record type )とよびます。

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

レコード型を定義するには、データ型としての名前、フィールドの名前、およびそれぞれのフィールドのデータ型を決めなくてはいけません。 上記のイメージの場合は、データ型としての名前をTime, フィールドの名前を hour minute , フィールドのデータ型を整数型(int型)と決めます。

実は、Javaにはレコードやレコード型はありません。 しかし、 インスタンス instance )や クラス class )というものが、代わりの機能を持っています。 一般的な用語とJavaの用語の対応は次の通りです。

レコードに関する用語の対応
一般 Java
レコード インスタンス
フィールド インスタンス変数
レコード型 クラス

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

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

インスタンスの使い方

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

class クラス名 {
    int フィールド名1;
    ...;
    int フィールド名n;
}

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

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

これをファイル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*/ }
24102a1:java1 k16x1001$ java TimeTest
9:30
24102a1:java1 k16x1001$

変数に整数(int型)を格納するには、変数の宣言が必要でした。 変数にインスタンスを格納するにも、同様に変数の宣言が必要です。 これは次のように書きます。

クラス名 変数名;

今まで int と書いていた部分をクラス名に変えます。 3行目で、変数 x をTimeクラスと宣言します。

クラスのインスタンスは、明示的に生成してはじめて使えます。 インスタンスを生成するには、キーワード new を用います。 「 クラス名 」のインスタンスを生成して、「 変数名 」に格納するには、

変数名 = new クラス名();

と書きます。 4行目で、Timeクラスのインスタンスを生成して、変数 x に格納します。

なお、次のように書くと、変数の宣言とインスタンスの生成、そしてその変数への格納が同時に行えます。

クラス名 変数名 = new クラス名();

インスタンスのフィールドにデータを格納するには、次のような文を用います。

変数名.フィールド名 = ;

これで、「 変数名 」に格納されているインスタンスの「 フィールド名 」に、「 」の値が格納されます。 5行目で、インスタンス x hour フィールドに9が格納され、6行目で、 minute フィールドに30が格納されます。

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

変数名.フィールド名

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

コンストラクタ

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

Time x = new Time();
x.hour = 9;
x.minute = 30;

配列では、初期化を使ってプログラムを短くしました。 インスタンスでも、 コンストラクタ constructor )というものを用いると、初期化に相当することができます。 上記の例なら、次のように書けます。

Time x = new Time(9, 30);

もし、クラスが

class クラス名 {
    int フィールド名1;
    ...;
    int フィールド名n;
}

と定義されていたら、次のように定義し直すと、コンストラクタが使えるようになります。

class クラス名 {
    int フィールド名1;
    ...;
    int フィールド名n;
    クラス名 (int フィールド名1, ..., int フィールド名n) {
        this.フィールド名1 = フィールド名1;
        ...
        this.フィールド名n = フィールド名n;
    }
}

これで、式

new クラス名(引数1, ..., 引数n)

を呼び出すと、「 フィールド名 1 」に「 引数 1 」を格納し、…、「 フィールド名 n 」に「 引数 n 」を格納したインスタンスになります。

コンストラクタはメソッドに似ています。 どちらも、あらかじめ定義しておき、呼出しによってその機能が働きます。 メソッドとコンストラクタは以下の点が異なります。

次のプログラムでは、コンストラクタも定義したクラスTime2を定義しています。 Time2.javaの4行目から7行目までが、コンストラクタ定義です。 このコンストラクタは、第1引数を hour フィールドに格納し、第2引数を minute フィールドに格納します。 Time2Test.javaの3行目で、このコンストラクタが呼び出され、 hour フィールドに9が格納され、 minute フィールドに30が格納されます。

Time2.java
/*  1*/ class Time2 { // 時刻2
/*  2*/     int hour;
/*  3*/     int minute;
/*  4*/     Time2 (int hour, int minute) { // コンストラクタ定義
/*  5*/         this.hour = hour;
/*  6*/         this.minute = minute;
/*  7*/     }
/*  8*/ }
Time2Test.java
/*  1*/ class Time2Test { // Time2クラスのテスト
/*  2*/     public static void main (String[] args) {
/*  3*/         Time2 x = new Time2(9, 30); // コンストラクタ呼出し
/*  4*/         System.out.println(x.hour + ":" + x.minute);
/*  5*/     }
/*  6*/ }
24102a1:java1 k16x1001$ java Time2Test
9:30
24102a1:java1 k16x1001$

注意1. 上記のコンストラクタは、最も単純なものです。 一般的には、コンストラクタはより複雑なことが行えます。

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

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

インスタンスの使用例

インスタンスの例として、以前考えた買い物の問題を再び取り上げます。

A子は店に行き、150円のペットボトルを1本と120円の缶ジュースを3本買いました。 A子はいくら支払わなければならないでしょうか。

/*  1*/ class SomeDrinks2 { // いくつかの飲み物2
/*  2*/     public static void main (String[] args) {
/*  3*/         int total, pet = 150, can = 120;
/*  4*/         total = pet + 3 * can;
/*  5*/         System.out.println(total);
/*  6*/     }
/*  7*/ }

このプログラムでは、値段だけを変数に格納し、個数は変数に格納しませんでした。 インスタンスを利用すれば、値段と個数をまとめて格納できます。

ファイルBuy.javaでは、買った物を格納するために、Buyというクラスを定義します。 price というフィールドには値段を格納し、 count というフィールドには個数を格納します。

/*  1*/ class Buy { // 買った物
/*  2*/     int price; // 値段
/*  3*/     int count; // 個数
/*  4*/ }

ファイルBuyMain.javaでは、Buyクラスの変数 pet を宣言し、Buyクラスのインスタンスを生成して pet に格納します。 変数 can についても同様に宣言・生成します。 インスタンス pet にはペットボトルの値段と個数を格納し、インスタンス can には缶ジュースの値段と個数を格納します。 最後に、合計金額を計算します。

/*  1*/ class BuyMain { // 買った物のメイン
/*  2*/     public static void main (String[] args) {
/*  3*/         int total;
/*  4*/         Buy pet = new Buy(); // 宣言と生成
/*  5*/         Buy can = new Buy(); // 宣言と生成
/*  6*/         pet.price = 150; // ペットボトルの値段は150円
/*  7*/         pet.count = 1; // ペットボトルの個数は1個
/*  8*/         can.price = 120; // 缶ジュースの値段は120円
/*  9*/         can.count = 3; // 缶ジュースの個数は3個
/* 10*/         total = pet.count * pet.price + can.count * can.price;
/* 11*/         System.out.println(total);
/* 12*/     }
/* 13*/ }
24102a1:java1 k16x1001$ java BuyMain
510
24102a1:java1 k16x1001$

続いて、コンストラクタを利用して、このプログラムを短くします。

ファイルBuy2.javaでは、クラスBuy2を定義します。 このクラスのコンストラクタは、引数が値段、個数の順になるように定義します。

/*  1*/ class Buy2 { // 買った物2
/*  2*/     int price;
/*  3*/     int count;
/*  4*/     Buy2 (int price, int count) { // コンストラクタ定義
/*  5*/         this.price = price;
/*  6*/         this.count = count;
/*  7*/     }
/*  8*/ }

ファイルBuy2Main.javaでは、コンストラクタを呼び出して、Buy2クラスの変数 pet にペットボトルの値段と個数を格納します。 同様に、Buy2クラスの変数 can に缶ジュースの値段と個数を格納します。 最後に、合計金額を計算します。

/*  1*/ class Buy2Main { // 買った物2のメイン
/*  2*/     public static void main (String[] args) {
/*  3*/         int total;
/*  4*/         Buy2 pet = new Buy2(150, 1); // コンストラクタ呼出し
/*  5*/         Buy2 can = new Buy2(120, 3); // コンストラクタ呼出し
/*  6*/         total = pet.count * pet.price + can.count * can.price;
/*  7*/         System.out.println(total);
/*  8*/     }
/*  9*/ }
24102a1:java1 k16x1001$ java Buy2Main
510
24102a1:java1 k16x1001$

試験について

シラバスに書いた通り、次回の授業中に試験を行います。 次回は欠席しないようにしてください。

試験は、授業の教室で、授業の後半の30分間で実施します。 試験の方法は筆記試験で、資料の持ち込みやパソコンの利用は不可とします。

以下に試験問題(全5問)のヒントを載せますので、試験勉強の参考にしてください。

1. 以下のJavaのプログラムは、割り算の割られる整数と割る整数を入力すると、整数の割り算をして、その結果と余りを求めるものです。 12行目以降の4か所を穴埋め問題にします。

/*  1*/ import java.io.*;
/*  2*/
/*  3*/ class IntegerDivision {
/*  4*/     public static void main (String[] args) throws IOException {
/*  5*/         InputStreamReader isr = new InputStreamReader(System.in);
/*  6*/         BufferedReader br = new BufferedReader(isr);
/*  7*/         int dividend, divisor, quotient, remainder;
/*  8*/         System.out.print("割られる整数を入力してください: ");
/*  9*/         dividend = Integer.parseInt(br.readLine());
/* 10*/         System.out.print("割る整数を入力してください: ");
/* 11*/         divisor = Integer.parseInt(br.readLine());
/* 12*/         quotient = dividend / divisor;
/* 13*/         remainder = dividend % divisor;
/* 14*/         System.out.println("割り算の結果は " + quotient + " です。");
/* 15*/         System.out.println("余りは " + remainder + " です。");
/* 16*/     }
/* 17*/ }
24102a1:java1 k16x1001$ java IntegerDivision
割られる整数を入力してください: 7
割る整数を入力してください: 2
割り算の結果は 3 です。
余りは 1 です。
24102a1:java1 k16x1001$ java IntegerDivision
割られる整数を入力してください: 6
割る整数を入力してください: 2
割り算の結果は 3 です。
余りは 0 です。
24102a1:java1 k16x1001$

2. 以下のJavaのプログラムは、入力された3つの整数 x , y , z の最大値を求めるものです。 x が他の整数以上ならば x が最大値、 y が他の整数以上ならば y が最大値、どちらでもないならば z が最大値とします。 14行目以降の4か所を穴埋め問題にします。

/*  1*/ import java.io.*;
/*  2*/
/*  3*/ class Maximum3 {
/*  4*/     public static void main (String[] args) throws IOException {
/*  5*/         InputStreamReader isr = new InputStreamReader(System.in);
/*  6*/         BufferedReader br = new BufferedReader(isr);
/*  7*/         int x, y, z, max;
/*  8*/         System.out.print("整数を入力してください: ");
/*  9*/         x = Integer.parseInt(br.readLine());
/* 10*/         System.out.print("整数を入力してください: ");
/* 11*/         y = Integer.parseInt(br.readLine());
/* 12*/         System.out.print("整数を入力してください: ");
/* 13*/         z = Integer.parseInt(br.readLine());
/* 14*/         if (x >= y && x >= z) {
/* 15*/             max = x;
/* 16*/         } else if (y >= x && y >= z) {
/* 17*/             max = y;
/* 18*/         } else {
/* 19*/             max = z;
/* 20*/         }
/* 21*/         System.out.println("最大値は " + max + " です。");
/* 22*/     }
/* 23*/ }
24102a1:java1 k16x1001$ java Maximum3
整数を入力してください: 1
整数を入力してください: 2
整数を入力してください: 3
最大値は 3 です。
24102a1:java1 k16x1001$

3. 以下のJavaのプログラムは、縦の個数と横の個数を入力すると、アステリスク記号を箱型に画面出力するものです。 ループ変数 i j を使って2重ループにしています。 12行目以降の4か所を穴埋め問題にします。

/*  1*/ import java.io.*;
/*  2*/
/*  3*/ class AsteriskBox {
/*  4*/     public static void main (String[] args) throws IOException {
/*  5*/         InputStreamReader isr = new InputStreamReader(System.in);
/*  6*/         BufferedReader br = new BufferedReader(isr);
/*  7*/         int i, j, height, width;
/*  8*/         System.out.print("縦の個数を入力してください: ");
/*  9*/         height = Integer.parseInt(br.readLine());
/* 10*/         System.out.print("横の個数を入力してください: ");
/* 11*/         width = Integer.parseInt(br.readLine());
/* 12*/         for (i = 0; i < height; i++) {
/* 13*/             for (j = 0; j < width; j++) {
/* 14*/                 System.out.print("*");
/* 15*/             }
/* 16*/             System.out.println();
/* 17*/         }
/* 18*/     }
/* 19*/ }
24102a1:java1 k16x1001$ java AsteriskBox
縦の個数を入力してください: 3
横の個数を入力してください: 5
*****
*****
*****
24102a1:java1 k16x1001$

4. 以下のJavaのプログラムは、当選番号を12, 34, 56, 78, 90として、入力された抽選番号が当たりかハズレかを判断するものです。 当選番号は配列 winning に格納します。 変数 count を用意し、入力された抽選番号と一つ一つの当選番号を比較し、一致していたら count を1増加させます。 最後に、 count が正であれば当たり、そうでなければハズレとします。 11行目以降の4か所を穴埋め問題にします。

/*  1*/ import java.io.*;
/*  2*/
/*  3*/ class WinningNumbers {
/*  4*/     public static void main (String[] args) throws IOException {
/*  5*/         InputStreamReader isr = new InputStreamReader(System.in);
/*  6*/         BufferedReader br = new BufferedReader(isr);
/*  7*/         int i, lottery, count = 0;
/*  8*/         int[] winning = {12, 34, 56, 78, 90};
/*  9*/         System.out.print("抽選番号を入力してください: ");
/* 10*/         lottery = Integer.parseInt(br.readLine());
/* 11*/         for (i = 0; i < winning.length; i++) {
/* 12*/             if (lottery == winning[i]) {
/* 13*/                 count++;
/* 14*/             }
/* 15*/         }
/* 16*/         if (count > 0) {
/* 17*/             System.out.println("当たりです。");
/* 18*/         } else {
/* 19*/             System.out.println("ハズレです。");
/* 20*/         }
/* 21*/     }
/* 22*/ }
24102a1:java1 k16x1001$ java WinningNumbers
抽選番号を入力してください: 56
当たりです。
24102a1:java1 k16x1001$ java WinningNumbers
抽選番号を入力してください: 55
ハズレです。
24102a1:java1 k16x1001$

5. 以下のJavaのプログラムは、同じ数字を繰り返した整数を作る関数 repeat を定義して、数字5が2回で55, 数字3が3回で333, 数字2が5回で22222を画面出力するものです。 整数の作り方は、数字5が2回ならば、最初は0で、それを10倍して5を足せば5になり、それを10倍して5を足せば55になるというものです。 7行目以降の4か所を穴埋め問題にします。

/*  1*/ class RepeatDigit {
/*  2*/     public static void main (String[] args) {
/*  3*/         System.out.println("repeat(5, 2) = " + repeat(5, 2));
/*  4*/         System.out.println("repeat(3, 3) = " + repeat(3, 3));
/*  5*/         System.out.println("repeat(2, 5) = " + repeat(2, 5));
/*  6*/     }
/*  7*/     static int repeat (int digit, int count) {
/*  8*/         int i, number = 0;
/*  9*/         for (i = 0; i < count; i++) {
/* 10*/             number = 10 * number + digit;
/* 11*/         }
/* 12*/         return number;
/* 13*/     }
/* 14*/ }
24102a1:java1 k16x1001$ java RepeatDigit
repeat(5, 2) = 55
repeat(3, 3) = 333
repeat(2, 5) = 22222
24102a1:java1 k16x1001$

アンケート(1)

次回の授業中に、授業評価のアンケートを実施します。 アンケートはマークシート方式なので、鉛筆かシャープペンシルを持参してください。


演習13

ある小学校で国語と算数の試験が行われました。 太郎君は国語が70点、算数が80点でした。 花子さんは国語が90点、算数が100点でした。 インスタンスを利用して、これらの得点データを格納し、全体の平均点(小数点以下切り捨て)を計算してください。

24102a1:java1 k16x1001$ java ScoreMain
85
24102a1:java1 k16x1001$

Score.javaでは、試験の得点を格納するするために、クラスScoreを定義します。 フィールド japanese には国語の得点を格納し、フィールド arithmetic には算数の得点を格納します。

ScoreMain.javaでは、Scoreクラスの変数 taro hanako を宣言・生成します。 インスタンス taro には太郎君の得点を格納し、インスタンス hanako には花子さんの得点を格納します。 最後に、すべての得点を合計し、4で割ります。

余力のある人は、Score2.javaで、コンストラクタも定義したクラスScore2を定義してください。

Score2Main.javaでは、コンストラクタを呼び出して、Score2クラスの変数 taro には太郎君の得点を格納し、Score2クラスの変数 hanako には花子さんの得点を格納します。 最後に、すべての得点を合計し、4で割ります。

24102a1:java1 k16x1001$ java Score2Main
85
24102a1:java1 k16x1001$

レポート課題

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


参考文献


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

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