クラスの階層

2.5 クラスの階層

 継承を行う事でスーパークラスやサブクラスといった親子関係が発生し、クラスに階層が出来る事は学習しました。さらに本節までに習った抽象クラスやインタフェースを利用するとより高度な階層構造を作ることもできるようになります。その仕組みについて説明していきます。

2.5.1 多重継承の仕組みについて

 プログラムを作成していく内に、2つ以上のクラスを同時に継承させてサブクラスを使いたいという場合が出てくるかもしれません。このような場合の継承を多重継承(multiple inheritance)と言います。このような場合どのようにすれば多重継承を実現できるのでしょうか。その答えはJavaでは「クラスの多重継承はできない」事になっています。1章「クラスの継承とオーバーライド」で、スーパークラスを2つ以上もつサブクラスを宣言することはできないと既に示しています。

図 2.5.1: クラスの多重継承の禁止

 クラスを使った多重継承は行えませんが、インタフェースの仕組みで多重継承(実装)が可能になることは前述しています。インタフェースを使うと、多重継承の仕組みの一部を実現する事が可能です。

図 2.5.2: インタフェースの多重継承(実装)は可能

2.5.2 2つ以上のインタフェースを実装する方法

 クラスには2つ以上のインタフェースを実装することが可能な事は前節で説明しました。
 実際に定義する基本構文を以下に示します。

 上記の凡例は2つのインタフェースを実装していますが、もっと多くの数でも実装できます。両方のインタフェースに定義されている抽象メソッドを必ず実装しなければならないのは、多重継承(実装)になっても変わりません。

ポイント
  • インタフェースはクラスと違い多重継承(実装)が可能。複数実装する場合は全ての抽象クラスの定義を行う必要がある。

 次の項で、インタフェースを多重継承(実装)したクラスを利用したプログラムを紹介します。

2.5.3 インタフェースを多重継承(実装)した新しいクラスを利用するプログラム

 インタフェースを多重継承(実装)した新しいクラスを作成し、その機能が正しく動作する事を確認します。

➢ IMachinery.java ※インタフェース(機械)

 2.4.2で作成したクラスを利用します。

① ソース・フォルダー      :myproj_basic/src
② パッケージ          :jp.co.f1.basic.ch12
③ 名前             :IProduction

➢ IProduction.java ※インタフェース(製造)
package jp.co.f1.basic.ch12;

public interface IProduction {
	void pShow(); // 抽象メソッド
}

解説

 このインタフェースは抽象メソッドであるpShow()メソッドのみを定義しています。
   3:public interface IProduction {
   4: void pShow(); //抽象メソッド
   5:}

① ソース・フォルダー      :myproj_basic/src
② パッケージ          :jp.co.f1.basic.ch12
③ 名前             :Computer3

➢ Computer3.java ※インタフェース(機械、製造)2つ実装したクラス
package jp.co.f1.basic.ch12;

//インタフェースMachinery,IProductionを実装
public class Computer3 implements IMachinery, IProduction {

	private String os;
	private int memory;

	// コンストラクタ(引数あり)
 	public Computer3(String os, int memory) {
 		this.os = os;
 		this.memory = memory;
 		System.out.println("OS「" + os + "」メモリサイズ「" + memory
 				+ "MByte」のパソコンを作成しました。");
 	}
 	// IMachineryの抽象メソッドをオーバーライド
 	public void show() {
 		System.out.println("パソコンのOSは「" + os + "」です。");
 		System.out.println("メモリサイズは「" + memory + "MByte」です。");
 		System.out.println("製造元は「" + MANUFACTURER + "」です。");
 	}
 	// IProductionの抽象メソッドをオーバーライド
 	public void pShow() {
 		System.out.println("このパソコンの製造は2011/1/1です。");
 	}
 }

解説

 このComputer3クラスは4行目の記述で、2つのインタフェースIMachineryとIProductionを実装しています
   4:public class Computer3 implements IMachinery , IProduction {

 17~21行目でインタフェースIMachineryの抽象メソッドの、show()メソッドをオーバーライドして処理を実装しています。
   17:public void show(){
   18: System.out.println("パソコンのOSは「" + os + "」です。");
   19: System.out.println("メモリサイズは「" + memory + "MByte」です。");
   20: System.out.println("製造元は「" + MANUFACTURER + "」です。");
   21:}

 23~25行目のインタフェースIProductionの抽象メソッドの、pShow()メソッドをオーバーライドして処理を実装しています。
   23:public void pShow(){
   24: System.out.println("このパソコンの製造は2011/1/1です。");
   25:}

① ソース・フォルダー      :myproj_basic/src
② パッケージ          :jp.co.f1.basic.ch12
③ 名前             :UseMultipleInterfaceClass
④ 作成するメソッド・スタブの選択:public static void main(String[] args) にチェックを入れる

➢ UseMultipleInterfaceClass.java ※mainメソッドを持つ実行クラス
package jp.co.f1.basic.ch12;

public class UseMultipleInterfaceClass {
public static void main(String[] args) {
	// Computer3クラスをオブジェクト化
	System.out.println("[Computer3オブジェクトの作成]");
	Computer3 com = new Computer3("Windows7", 3072);
	System.out.println("---------------------------------------");

	// Computer3オブジェクトの情報を表示
	System.out.println("■Computer情報を表示");
	com.show();
	com.pShow();
}
}

実行結果

解説

 7行目でインタフェースを2つ実装しているComputerクラスのオブジェクト化を、引数ありのコンストラクタで行っています。
   7:Computer3 com = new Computer3("Windows7", 3072);

 7行目処理は実行結果を見て分かるように、正しく実行されているのが確認できます。
   [Computer3オブジェクトの作成]
   OS「Windows7」メモリサイズ「3072MByte」のパソコンを作成しました。 //7行目の処理結果

 12、13行目処理ではインタフェースIMachineryとIProductionで定義されている抽象メソッドを、オーバーライドしたshow()メソッドとpShow()メソッドを呼び出してパソコン情報と製造情報を表示しています。
   12:com.show();
   13:com.pShow();

 12、13行目の処理は実行結果を見て分かるように、正しく各クラスでオーバーライドしたshow()メソッドが実行されているのが確認できます。

 今回のプログラムでComputer3クラスは、2つのインタフェースを実装しています。そのため、Computer3クラスでは両方のインタフェースの抽象メソッドの処理内容を定義しなければなりません。Javaではクラスの多重継承を認めていませんが、2つ以上のインタフェースを実装することで、メソッド定義(名前、戻り値の型、引数の形式)を多重継承することができます。

図 2.5.3: インタフェースの多重継承(実装)はメソッド定義を継承

ポイント
  • インタフェースの多重継承(実装)は、インタフェースで定義された抽象クラスの定義を継承する。

 次の項ではインタフェースの応用方法を紹介します。

2.5.4 インタフェースの応用方法

 インタフェースの特徴で前述しましたが、インタフェースの実装(implements)とクラスの継承(extends)を併用できる事と、クラス同様に拡張して新しいインタフェースを宣言することが可能になっています。その方法について説明していきます。

書式:クラス継承とインタフェースの実装の併用
 extends→implementsの順で定義します。継承元クラスは1つ可能で、通常のクラスまたは抽象クラスどちらでもかまいません。インタフェースも当然多重定義(実装)可能になっています。

 凡例:クラス継承とインタフェースの実装の併用
 インタフェースを複数実装したい場合は「,(カンマ)」で区切ってインタフェース名を記述すれば可能になります。

図 2.5.4: インタフェースの実装とクラスの継承を併用する

書式:インタフェースの継承
 継承元をスーパーインタフェース、継承先をサブインタフェースといい、クラスの継承と同様にインタフェースを継承する場合もextends修飾子を利用して行います。クラスと違い多重継承を許可している点も合わせて覚えておいて下さい。

凡例:インタフェースの継承

 サブインタフェースを実装する場合も、通常のインタフェース同様にimplementsで実装します。さらに実装したインタフェースのメソッドだけではなく、継承元のスーパーインタフェースのメソッドもクラスで定義する必要があるので注意が必要です。

図 2.5.5: インタフェースの継承とサブインタフェースの実装

ポイント
  • 1つのクラスでインタフェースの実装(implements)とクラスの継承(extends)と併用して行える。
  • インタフェースはクラスと同様に他のインタフェースを継承することができる。

 本章で紹介してきたプログラムでは抽象クラスやインタフェースの有効性を感じる事はできなかったのではないでしょうか。この2つの機能は大規模なシステム開発を行う際に利用してこそ真価が発揮できます。抽象クラスとインタフェースを使えば多くのクラスをまとめて扱えるようにもなり、設計段階でクラスの枠組みを提供する事で作成のミスも防ぐ事が可能になります。
 抽象クラスで共通機能を提供し、抽象クラスだけではクラス階層が実現できない場合はインタフェースの多重継承(実装)を利用すれば可能になります。
 この機能をどのように活用していくかについては様々な手法があります。本章で学習した内容では抽象クラス・インタフェースの性質をおさえ、実践の場で活かせる基礎として下さい。

複数のインタフェースを実装する場合の注意点

 インタフェースには定数を定義することができます。そのため実装した複数のインタフェースに同名の定数が定義されてしまう場合があります。このとき、同じ名前の定数を持っているインタフェースをimplementsしただけではエラーにならず、定数を参照することではじめてエラーが発生します。
 途中までは何の問題もないように動いていたプログラムが定数を参照することで、突然エラーが発生するというケースもあるため注意が必要です。
 なお、複数のインタフェースに同じ名前の定数が定義されていた場合、完全限定名で利用するなど参照する定数を明確に指定することでエラーを回避することも可能です。

クラスの継承とインタフェースの実装の併用時の注意点

 通常、インタフェースに定義された抽象メソッドは、implementsされたクラスに処理内容を記述する必要があります。しかしimplementsされたクラスが別クラスを継承していて、かつ、そのスーパークラス内で同名のメソッドが定義されている場合には、スーパークラスで定義されたメソッドを継承しているため、implementsされたクラスで定義していなくてもエラーは発生しなくなります。


NEXT>> 2.6 本章のまとめ