インタフェース型変数の代入

Revised: Feb./23rd/2003: Since: Dec./31st/2001

"is-a" 関係

通常のクラスの場合は、インスタンス化により作られるオブジェクトを参照する ID を代入する変数をクラス型変数として宣言します。

インタフェース型の変数も定義できます。インタフェース型で宣言された参照型変数には、そのインタフェースを実装するクラスから作られた全てのオブジェクトの参照を代入できます。

クラス型変数の場合、当該クラスから生成されたオブジェクトのみならず、サブクラスから生成されたオブジェクトを代入できます。インタフェース型オブジェクトの場合、実装クラスから生成されたオブジェクトを代入できます。この意味で、スーパークラスの継承とインタフェースの実装は、何れも "is-a" 関係にあると呼ばれます。

例えば、哺乳類クラス Mammal を継承して猫クラス Cat を作る場合、「猫は哺乳類である。」(A Cat is a Mammal.) という関係にあります。名づけられるオブジェクトの生成元クラスを識別するインタフェース Nameable を実装して猫クラス Cat を作る場合、「猫は命名可能である。」(A Cat is Nameable.) という関係にあるわけです。

"is-a" 関係は、「~は~である」、「~は~の一種である」という関係を指します。これは、コンパイラが「~と~は代入互換である」と認識することを意味します。つまり、サブクラス型オブジェクトはスーパークラス型変数に代入可能です。実装クラス型オブジェクトはインタフェース型変数に代入可能です。

多態性

オブジェクトは、各々が自分の責任で動作します。つまり、引数などで動作を特定しなくとも、自分がなすべきことをオブジェクトが知っているのです。これを多態性(ポリモーフィズム)と呼びます。

具体的に説明します。オブジェクトをスーパークラス型やインタフェース型に代入してメソッド呼び出しをすると、各々が生成されたクラス型に応じて適切に動作します。つまり、同じクラス型、インタフェース型のオブジェクトに対して同じメソッドを呼び出しても、そのオブジェクトが生成されたオリジナルのクラスに応じて、異なる動作をするわけです。

例えば、Drawable インタフェースに draw() メソッドを定義して、これを Rectangle, Circle、Triangle で実装するとします。このとき、Rectangle オブジェクトや Circle オブジェクトは Drawable 型変数に代入可能です。

Drawable[] shapes = { new Rectangle(), new Circle(), new Triangle() };

これらの具象クラスでは draw() が別々に実装されており、その動作は異なります。Drawable 型変数 shapes に対して draw() を呼び出すと、代入されているオブジェクトのオリジナル・クラスでの実装に応じて異なる動作をするわけです。

shapes[0].draw();	// 長方形を描く
shapes[1].draw();	// 丸を描く
shapes[2].draw();	// 三角形を描く

これが多態性(ポリモーフィズム)です。

サンプル

次の例では、一つのインタフェースと二つの実装クラス、及びそれらをインスタンス化する一つのクラスが定義されています。

main() メソッドからは、インタフェース型引数を持つメソッドに、当該インタフェースを実装したクラスのオブジェクトを渡しています。

TestInterface4.java:

// インタフェース
interface Circle {
	String calc(double d);
}
// 実装クラスその1
class Circum implements Circle {
	public String calc(double r) {
		double circum = 2.0 * Math.PI * r;
		return "円周:" + circum;
	}
}
// 実装クラスその2
class Area implements Circle {
	public String calc(double r) {
		double area = Math.PI * r * r;
		return "面積:" + area;
	}
}
// コントロールクラス
class TestInterface4 {
	// main()メソッド
	public static void main(String[] args) {
		Circum obj1 = new Circum();
		Area obj2 = new Area();
		// メソッドへのオブジェクトの引渡し
		en(obj1);
		en(obj2);
	}
	// インタフェース型引数を取るメソッド
	static void en(Circle obj) {
		System.out.print("半径:" + 10 + " ");
		System.out.println(obj.calc(10));
	}
}

インタフェース型変数 Circle obj は、当該インスタンスを実装したクラスのインスタンスを全て参照できますので、異なるクラスをインスタンス化したオブジェクト Circum obj1, Area obj2 でも、変数の型として一致します。

C:\java>javac TestInterface4.java
C:\java>java TestInterface4
半径:10 円周:62.83185307179586
半径:10 面積:314.1592653589793
C:\java>

サンプル

次のコード CountableDemo.java は、インタフェース Countable を実装する二つのクラス Car と Dog をインスタンス化し、 Countable 型変数への代入、インタフェース・メソッドの利用を実装しています。

interface Countable {
	int getCount();
	void setCount(int aCounter);
}
class Dog implements Countable {
	private int counter;
	public int getCount() {
		return counter;
	}
	public void setCount(int aCounter) {
		counter = aCounter;
	}
}
class Car implements Countable {
	private int number;
	public int getCount() {
		return number;
	}
	public void setCount(int aCounter) {
		number = aCounter;
	}
}
class CountableDemo {
	public static void main(String[] args) {
		Dog dog = new Dog();
		Car car = new Car();
		
		// is-a 関係
		Countable[] objs = {dog, car};
		
		// インタフェース・メソッドの利用
		objs[0].setCount(10);
		objs[1].setCount(2);
		
		for (int i = 0; i < objs.length; i++) {
			// インタフェース・メソッドの利用
			System.out.println(objs[i].getCount());
		}
	}
}


Copyright © 2001, 2003 SUGAI, Manabu. All Rights Reserved.