#pragma twice

KAB-studio > プログラミング > Javaのオブジェクト指向入門 > 10. インタフェースという「クラスじゃないもの」 > 10.6 インタフェースはインスタンスを作らない!
 
前のページへつぎ

10.6 インタフェースはインスタンスを作らない!

del.icio.us 登録する はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数 livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数 Yahoo!ブックマーク 詳細を表示 users RSSに登録
更新日: 2008/04/28
動作確認環境:Windows XP Professional SP2, Java SE 5

インタフェースはインスタンスを作らない!

 前ページでは、インタフェースとクラスは別、ということを説明しました。
 では、なぜこのように、クラスとインタフェースは分けられているのでしょう。
 それは、インタフェースがインスタンスを作らないからです。

 クラスは、インスタンスを作るための「設計図」です。newでインスタンスを作るための情報、それがクラスです。
 ところが、インタフェースにはその機能がありません
 インタフェースはインスタンスを作りません。
 まず、インタフェース単体では当然インスタンスを作れません。
 また、インタフェースの実装クラスを元にインスタンスを作っても、その中に「インタフェースから作られたインスタンス」は含まれていません。
 それを、プログラムを通して見てみましょう。

// InheritedFromObjectRunner.java

/**
 * インタフェース。
 */
interface HasToStringInterface
{
    /**
     * 抽象メソッド。
     * 何か分からないけど文字列を返します。
     */
    String toString();
}

/**
 * Objectクラスのサブクラスで、かつ
 * HasToStringInterfaceインタフェースの実装クラス。
 */
class InheritedFromObject implements HasToStringInterface
{
    // 中身ナシ!
}

/**
 * 実行用クラス。このクラスを実行してください。
 */
class InheritedFromObjectRunner
{
    public static void main( String[] args )
    {
        // InheritedFromObjectクラスのインスタンスを1つ作ります。
        // 参照型変数の型はHasToStringInterfaceインタフェースです。
        HasToStringInterface ref = new InheritedFromObject();

        // toString()メソッドを呼び出します。
        String s = ref.toString();
        System.out.println( s );
        // 実行結果:
        // InheritedFromObject@1c6f579
        // (値は毎回変わります)
    }
}
// InheritedFromObjectRunner.java
/**
 * インタフェース。
 */
interface HasToStringInterface
{
	/**
	 * 抽象メソッド。
	 * 何か分からないけど文字列を返します。
	 */
	String toString();
}
/**
 * Objectクラスのサブクラスで、かつ
 * HasToStringInterfaceインタフェースの実装クラス。
 */
class InheritedFromObject implements HasToStringInterface
{
	// 中身ナシ!
}
/**
 * 実行用クラス。このクラスを実行してください。
 */
class InheritedFromObjectRunner
{
	public static void main( String[] args )
	{
		// InheritedFromObjectクラスのインスタンスを1つ作ります。
		// 参照型変数の型はHasToStringInterfaceインタフェースです。
		HasToStringInterface ref = new InheritedFromObject();
		// toString()メソッドを呼び出します。
		String s = ref.toString();
		System.out.println( s );
		// 実行結果:
		// InheritedFromObject@1c6f579
		// (値は毎回変わります)
	}
}

 まずHasToStringInterfaceインタフェースを用意します。
 このインタフェースは抽象メソッドをひとつ持っています。

/**
 * インタフェース。
 */
interface HasToStringInterface
{
    /**
     * 抽象メソッド。
     * 何か分からないけど文字列を返します。
     */
    String toString();
}
/**
 * インタフェース。
 */
interface HasToStringInterface
{
	/**
	 * 抽象メソッド。
	 * 何か分からないけど文字列を返します。
	 */
	String toString();
}

 次に、このインタフェースを実装したInheritedFromObjectクラスを作ります。

/**
 * Objectクラスのサブクラスで、かつ
 * HasToStringInterfaceインタフェースの実装クラス。
 */
class InheritedFromObject implements HasToStringInterface
{
    // 中身ナシ!
}
/**
 * Objectクラスのサブクラスで、かつ
 * HasToStringInterfaceインタフェースの実装クラス。
 */
class InheritedFromObject implements HasToStringInterface
{
	// 中身ナシ!
}

 このクラス、HasToStringInterfaceインタフェースを実装しておきながら、toString()メソッドを実装していません
 でもこのクラスはこれでちゃんと使えます。
 なぜかというと、InheritedFromObjectクラスは自動的にObjectクラスから継承していて、そのObjectクラスがtoString()メソッドを持っているからです。
 「8.2 Objectクラスってなに?」で説明したとおり、すべてのクラスはObjectクラスのサブクラスになるため、InheritedFromObjectクラスも自動的にObjectクラスのサブクラスになります。
 そして、そのObjectクラスは「public String toString()」メソッドを持っています。これが、HasToStringInterfaceインタフェースのtoString()メソッドの実装になります
 これまでは、抽象メソッドの実装は、実装先のクラスで作っていました。でも実は、スーパークラスに抽象メソッドと同じ名前・引数のメソッドがあればそれで実装できちゃうんです

 では、InheritedFromObjectクラスのインスタンスを作ってみましょう。

        // InheritedFromObjectクラスのインスタンスを1つ作ります。
        // 参照型変数の型はHasToStringInterfaceインタフェースです。
        HasToStringInterface ref = new InheritedFromObject();
		// InheritedFromObjectクラスのインスタンスを1つ作ります。
		// 参照型変数の型はHasToStringInterfaceインタフェースです。
		HasToStringInterface ref = new InheritedFromObject();

 InheritedFromObjectクラスのインスタンスが作られると、そのインスタンスの中に、Objectクラスのインスタンスと、HasToStringInterfaceインタフェースが含まれます。
 この時、HasToStringInterfaceインタフェースのtoString()メソッドは、ObjectクラスのtoString()メソッドにひもを付けます。

 この時、インタフェースはインスタンスを作りません。あくまで「インタフェース」としてインスタンスの中に入っています。
 インタフェースはフィールドもメソッドも持ちません。そのため、「インスタンスを作ってその中にフィールドやメソッドを入れる」ということをする必要がありません。つまりインタフェースは入れる物がないんです。普通のクラスが持つフィールドやメソッドといった「インスタンスの中に入れる物」が、インタフェースにはないんです。
 インタフェースは抽象メソッドを持っています。この抽象メソッドは中身がありません。では何のためにあるかというと、単に実装メソッドにひもを付けているだけです。ひもそのものと言ってもいいかもしれません。
 つまり、インタフェースはメソッドにひもを付けるためのものなのです。
 そのため、新しくメソッドを作らなくても、このようにスーパークラスのメソッドにひもを付けることができ、インタフェースを通してそれを呼び出すことができるわけです。

        // toString()メソッドを呼び出します。
        String s = ref.toString();
        System.out.println( s );
        // 実行結果:
        // InheritedFromObject@1c6f579
        // (値は毎回変わります)
		// toString()メソッドを呼び出します。
		String s = ref.toString();
		System.out.println( s );
		// 実行結果:
		// InheritedFromObject@1c6f579
		// (値は毎回変わります)

 インタフェースの参照型変数の抽象メソッドを呼び出すと、ひもが付いている実装メソッドが呼び出されます。
 インタフェースを実装したことで、そのひもを通して操作ができるようになったわけです。

 このように、インタフェースは単なるひも。実装したメソッドを呼び出すためだけのものです。
 インタフェースは「中身」がなく、そのためインスタンスを作る必要がありません。なのでインスタンスを持たず、ただひもだけ付けるわけです。

中身がないからいっぱい付けれる!

 「6.3 2つのクラスをくっつけられる?」で、クラスは「2つのクラスから1つのクラスを作れない」ということを説明しました。
 インタフェースにその制約がないのは、この「中身がない」という特徴が理由です。
 多重継承したクラスは、インスタンスの中にいろんなインスタンスがごちゃ混ぜに入って、その管理が難しくなります。
 でもインタフェースはいくら実装しても問題ありません。だってそのインスタンスがないんですから
 つまり、いくらインタフェースを実装しても、「フィールド重複してるんじゃね?」「どのフィールドを使う?」「どのメソッドが呼び出される?」といった問題がインスタンスを作らないため起きません。
 インタフェースを実装すると、ただひもが付けられるだけ。何も増えません。抽象メソッドは実装メソッドと見た目(名前・引数)が同じなので、いくら抽象メソッドが増えてひもが増えても、実質なにも増えたことにはならないのです。なので、いくら実装しても大丈夫、というわけです。

 ちなみに、インタフェースの抽象メソッドが自動的にpublicになるのは、これが理由です。
 インタフェースの抽象メソッドは「外から呼び出せるようひもを付ける」ためのものです。外から呼び出す、という目的のため自動的にpublicが付いているわけです。
 中身がなく、ただ外から呼び出すためだけの機能、それがインタフェースというわけです。

10.6 インタフェースはインスタンスを作らない!
このページは、Java言語を用いたオブジェクト指向プログラミングのチュートリアル解説を行う「Javaのオブジェクト指向入門」の一ページです。
詳しい説明は「Javaのオブジェクト指向入門」目次をご覧ください。