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

コンピュータIIIB(Javaアルゴリズム)第10回

目次
10.1 継承(1)
10.1.1 継承とは
10.1.2 継承の例
10.2 演習10
10.3 レポート課題
10.4 参考文献
索引
継承   サブクラス   スーパークラス   多重継承   単一継承  

10.1 継承(1)

10.1.1 継承とは

オブジェクト指向プログラミングにおけるオブジェクトとは、処理対象の性質や機能をモデル化した、変数と関数・手続きの構造であることは、前回説明した通りです。 オブジェクトを表すデータ型はクラスと呼ばれ、クラスに属するオブジェクトはインスタンスと呼ばれました。 また、オブジェクトの変数はインスタンス変数と呼ばれ、オブジェクトの関数と手続きはインスタンス・メソッドと呼ばれました。

ここで、オブジェクトの例として腕時計を考えます。 一般的な腕時計には時針と分針があります。 腕時計の性質は、何時何分を覚えていることであり、腕時計の機能は、何時何分が読み取れることと、何時何分が調節できることです。 腕時計を表すクラスを Watch とします。 このクラスでは、何時何分を覚えるインスタンス変数が用意され、何時何分を読み取るインスタンス・メソッドと、何時何分を調節するインスタンス・メソッドが定義されます。

腕時計の中には秒針を持つものもあります。 秒針を持つ腕時計をクラス ThreeHandWatch で表します。 秒針を持つ腕時計の性質と機能は、一般的な腕時計に似ています。 違いは、何秒に関する部分が追加されることです。 したがって、クラス ThreeHandWatch のインスタンス変数とインスタンス・メソッドは、クラス Watch のインスタンス変数とインスタンス・メソッドと似たように定義されます。

性質と機能が似る理由は、秒針を持つ腕時計は腕時計の特殊なものであり、特殊なものは一般的なものから多くの性質と機能を受け継ぐ傾向があるからです。 もし、一般的なものから性質と機能を受け継ぐ仕組みがあれば、同じことを何度も書かずに済むようになります。

オブジェクト指向プログラミングでは、クラスの間に階層関係を与えることができます。 この関係は、下位のクラスは上位のクラスより特殊なことを意味します。 上位のクラスは下位のクラスより一般的なことを意味するとも言えます。 上位のクラスは スーパークラスsuperclass ) と呼ばれ、下位のクラスは サブクラスsubclass ) と呼ばれます。

サブクラスは、スーパークラスのインスタンス変数とインスタンス・メソッドを受け継ぎます。 この、インスタンス変数とインスタンス・メソッドを受け継ぐ仕組みは、 継承inheritance ) と呼ばれます。 継承を利用しますと、サブクラスを定義するときは、スーパークラスとの違いのみを書けば済みます。 クラス ThreeHandWatch の場合ですと、クラス Watch と同じことを再び書くのではなく、クラス Watch をスーパークラスにし、何秒に関する部分のみを書くのです。

一つのクラスのスーパークラスが一つに限られる継承の仕組みは、 単一継承single inheritance ) と呼ばれます。 複数のスーパークラスが許される継承の仕組みは、 多重継承multiple inheritance ) と呼ばれます。 Javaは単一継承のプログラミング言語です。

単一継承であっても、一つのクラスのサブクラスは複数個になり得ます。 例えば、今日は何日かが分かる表示窓の付いた腕時計を考えます。 このクラスの名前を DayDisplayWatch とします。 このクラスは、何日に関する部分以外はクラス Watch と同じです。 したがって、クラス Watch をスーパークラスにし、何日に関する部分のみを書けば、このクラスの定義が完成します。 クラス ThreeHandWatch もクラス DayDisplayWatch も、クラス Watch のサブクラスです。

継承は何段階も行えます。 例として、何月何日という表示窓の付いた腕時計 CalendarWatch を定義します。 クラス Watch をスーパークラスにしても良いですが、クラス DayDisplayWatch をスーパークラスにしたほうがより良いです。 なぜならば、クラス Watch からは何時何分に関する部分が継承され、クラス DayDisplayWatch からは何日に関する部分が継承されるからです。 このクラスでは、何月に関する部分のみを書けば済みます。

以上の 4 つのクラスの階層関係を図で表しますと、次のようになります。

継承の関係
図 10.1  継承の関係

10.1.2 継承の例

それでは、クラス Watch から定義していきます。 何時何分を覚えるために、インスタンス変数 hourminute を用意します。 何時何分を読み取るために、インスタンス・メソッド getHourgetMinute を定義し、何時何分を調節するために、インスタンス・メソッド setHoursetMinute を定義します。

Watch.java
/*  1*/ class Watch {
/*  2*/     int hour, minute;
/*  3*/     int getHour () {
/*  4*/         return this.hour;
/*  5*/     }
/*  6*/     int getMinute () {
/*  7*/         return this.minute;
/*  8*/     }
/*  9*/     void setHour (int hour) {
/* 10*/         this.hour = hour;
/* 11*/     }
/* 12*/     void setMinute (int minute) {
/* 13*/         this.minute = minute;
/* 14*/     }
/* 15*/ }
WatchMain.java
/*  1*/ class WatchMain {
/*  2*/     public static void main (String[] args) {
/*  3*/         Watch w = new Watch();
/*  4*/         w.setHour(14);
/*  5*/         w.setMinute(55);
/*  6*/         System.out.print(w.getHour());
/*  7*/         System.out.print(":" + w.getMinute());
/*  8*/         System.out.println();
/*  9*/     }
/* 10*/ }
b04a001@AsiaA1:~/comp3b% java WatchMain
14:55
b04a001@AsiaA1:~/comp3b%

もし、継承を使わずにクラス ThreeHandWatch を定義しますと、次のようになります。

ThreeHandWatch.java
/*  1*/ class ThreeHandWatch {
/*  2*/     int hour, minute, second;
/*  3*/     int getHour () {
/*  4*/         return this.hour;
/*  5*/     }
/*  6*/     int getMinute () {
/*  7*/         return this.minute;
/*  8*/     }
/*  9*/     int getSecond () {
/* 10*/         return this.second;
/* 11*/     }
/* 12*/     void setHour (int hour) {
/* 13*/         this.hour = hour;
/* 14*/     }
/* 15*/     void setMinute (int minute) {
/* 16*/         this.minute = minute;
/* 17*/     }
/* 18*/     void setSecond (int second) {
/* 19*/         this.second = second;
/* 20*/     }
/* 21*/ }
WatchMain.java (第 2 版)
/*  1*/ class WatchMain {
/*  2*/     public static void main (String[] args) {
/*  3*/         ThreeHandWatch w = new ThreeHandWatch();
/*  4*/         w.setHour(14);
/*  5*/         w.setMinute(55);
/*  6*/         w.setSecond(30);
/*  7*/         System.out.print(w.getHour());
/*  8*/         System.out.print(":" + w.getMinute());
/*  9*/         System.out.print(":" + w.getSecond());
/* 10*/         System.out.println();
/* 11*/     }
/* 12*/ }
b04a001@AsiaA1:~/comp3b% java WatchMain
14:55:30
b04a001@AsiaA1:~/comp3b%

確かに、クラス ThreeHandWatch の定義の半分以上は、クラス Watch の定義と同じです。 継承を利用して、インスタンス変数とインスタンス・メソッドを受け継ぐべきです。

Javaでは、クラス A を定義するときに、クラス B をクラス A のスーパークラスにすることを、

class A extends B {
    ...
}

と書きます。

では、クラス ThreeHandWatch の定義を書き直します。 クラス ThreeHandWatch とクラス Watch の違いは、インスタンス変数 second が追加されている点と、インスタンス・メソッド getSecondsetSecond が追加されている点です。 したがって、それらのみを定義します。

ThreeHandWatch.java (第 2 版)
/*  1*/ class ThreeHandWatch extends Watch {
/*  2*/     int second;
/*  3*/     int getSecond () {
/*  4*/         return this.second;
/*  5*/     }
/*  6*/     void setSecond (int second) {
/*  7*/         this.second = second;
/*  8*/     }
/*  9*/ }
WatchMain.java (第 2 版再び)
/*  1*/ class WatchMain {
/*  2*/     public static void main (String[] args) {
/*  3*/         ThreeHandWatch w = new ThreeHandWatch();
/*  4*/         w.setHour(14);
/*  5*/         w.setMinute(55);
/*  6*/         w.setSecond(30);
/*  7*/         System.out.print(w.getHour());
/*  8*/         System.out.print(":" + w.getMinute());
/*  9*/         System.out.print(":" + w.getSecond());
/* 10*/         System.out.println();
/* 11*/     }
/* 12*/ }
b04a001@AsiaA1:~/comp3b% java WatchMain
14:55:30
b04a001@AsiaA1:~/comp3b%

クラス ThreeHandWatch では、インスタンス・メソッドは何秒に関するものしか定義していません。 しかし、そのインスタンスについては、何秒だけでなく、何時、何分に関するインスタンス・メソッドも呼び出せています。 これは、クラス ThreeHandWatch が、クラス Watch を継承したからです。

クラス DayDisplayWatch は次のように定義されます。

/*  1*/ class DayDisplayWatch extends Watch {
/*  2*/     int day;
/*  3*/     int getDay () {
/*  4*/         return this.day;
/*  5*/     }
/*  6*/     void setDay (int day) {
/*  7*/         this.day = day;
/*  8*/     }
/*  9*/ }

クラス CalendarWatch は次のように定義されます。

/*  1*/ class CalendarWatch extends DayDisplayWatch {
/*  2*/     int month;
/*  3*/     int getMonth () {
/*  4*/         return this.month;
/*  5*/     }
/*  6*/     void setMonth (int month) {
/*  7*/         this.month = month;
/*  8*/     }
/*  9*/ }
WatchMain.java (第 3 版)
/*  1*/ class WatchMain {
/*  2*/     public static void main (String[] args) {
/*  3*/         CalendarWatch w = new CalendarWatch();
/*  4*/         w.setMonth(12);
/*  5*/         w.setDay(9);
/*  6*/         w.setHour(14);
/*  7*/         w.setMinute(55);
/*  8*/         System.out.print(w.getMonth());
/*  9*/         System.out.print("/" + w.getDay());
/* 10*/         System.out.print(" " + w.getHour());
/* 11*/         System.out.print(":" + w.getMinute());
/* 12*/         System.out.println();
/* 13*/     }
/* 14*/ }
b04a001@AsiaA1:~/comp3b% java WatchMain
12/9 14:55
b04a001@AsiaA1:~/comp3b%

クラス CalendarWatch では、インスタンス・メソッドは何月に関するものしか定義していません。 しかし、そのインスタンスについては、何月だけでなく、何日、何時、何分に関するインスタンス・メソッドも呼び出せています。 これは、クラス CalendarWatch が、クラス Watch とクラス DayDisplayWatch を継承したからです。


10.2 演習10

展示会などに設置される、入場者カウンタについて、プログラムを作成してください。

この入場者カウンタは、展示会の入り口に設置され、センサーなどで入場者を数えるものです。 入場者カウンタを表すクラスは、 EntranceCounter とします。 入場者が増えることは、インスタンス・メソッド addEntrance で表すことにします。 また、カウンタの表示画面から入場者数を読み取ることは、インスタンス・メソッド getEntrance で表します。

入場者カウンタの特殊なものとして、退場者も数えるものを考えます。 これは、展示会の出入り口に設置され、入場者と退場者を数えます。 退場者も数える入場者カウンタのクラスは、 EntranceExitCounter とします。 このクラスは、クラス EntranceCounter のサブクラスとします。 退場者が増えることは、インスタンス・メソッド addExit で表すことにします。 また、カウンタの表示窓から退場者数を読み取ることは、インスタンス・メソッド getExit で表します。

今、展示会が始まったとします。 1人が入場しましたが、その人はすぐに退場しました。 また1人入場しましたが、その人もすぐ退場しました。 その後、1人入場しました。 この時点で、カウンタの表示画面を確認したところ、入場者数は3, 退場者数は2でした。 このことを、プログラムを作成して実現してください。

クラス EntranceCounter の定義プログラム、およびメインのプログラムは以下の通りとし、クラス EntranceExitCounter の定義プログラムを自作してください。

EntranceCounter.java
/*  1*/ class EntranceCounter {
/*  2*/     int entrance = 0;
/*  3*/     void addEntrance () {
/*  4*/         this.entrance++;
/*  5*/     }
/*  6*/     int getEntrance () {
/*  7*/         return this.entrance;
/*  8*/     }
/*  9*/ }
CounterMain.java
/*  1*/ class CounterMain {
/*  2*/     public static void main (String[] args) {
/*  3*/         EntranceExitCounter counter = new EntranceExitCounter();
/*  4*/         counter.addEntrance();
/*  5*/         counter.addExit();
/*  6*/         counter.addEntrance();
/*  7*/         counter.addExit();
/*  8*/         counter.addEntrance();
/*  9*/         System.out.println(counter.getEntrance());
/* 10*/         System.out.println(counter.getExit());
/* 11*/     }
/* 12*/ }
b04a001@AsiaA1:~/comp3b% java CounterMain
3
2
b04a001@AsiaA1:~/comp3b%

10.3 レポート課題

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


10.4 参考文献


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

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