#pragma twice

KAB-studio > プログラミング > Javaのオブジェクト指向入門 > 7. ポリモーフィズム!! > 7.5 インスタンスはどのクラス?
 
前のページへつぎ

7.5 インスタンスはどのクラス?

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

 前ページだけだと「ポリモーフィズム」がなんとなくわかっただけだと思うので、もう一つ例を紹介します。
 これを見れば、ポリモーフィズムがより不思議で変な機能だということがわかります。

どのインスタンスが使われる?

 前ページの例では、getInstance()メソッドに渡した値によってサブクラスを切り替える処理をしていました。
 でも、そのサンプルプログラムでは「最初に1、次に3」と決まっていました。ということはそのプログラムから見たら「別にポリモーフィズム使う必要なくね?」ということになります。だって、普通に「new PolySub1()」「new PolySub3()」ってすればいいだけですから。
 ではそれをもう一段階超えて、完全に「どのインスタンスを使うか分からない」という状態のポリモーフィズムを試してみましょう。

// Poly2Runner.java

/**
 * スーパークラス兼、インスタンス作成クラス。
 * (PolySuperクラスとほぼ同じくラスです)
 */
class PolySuper2
{
    /**
     * サブクラスのインスタンスを作って、その参照を返します。
     */
    static PolySuper2 getInstance( int number )
    {
        // 渡された番号によって作るインスタンスを変えます。
        if( number == 1 )
        {
            return new PolySub21();
        }
        else if( number == 2 )
        {
            return new PolySub22();
        }
        else if( number == 3 )
        {
            return new PolySub23();
        }
        else
        {
            return null;
        }
    }

    /**
     * 自クラスの名前を出力するメソッドです。
     */
    void printMyName()
    {
        System.out.println( "PolySuper2" );
    }
}

/**
 * PolySuper2クラスから継承したサブクラス x 3。
 * (PolySub*クラスとほぼ同じくラスです)
 */
class PolySub21 extends PolySuper2
{
    void printMyName()
    {
        System.out.println( "PolySub21" );
    }
}

class PolySub22 extends PolySuper2
{
    void printMyName()
    {
        System.out.println( "PolySub22" );
    }
}

class PolySub23 extends PolySuper2
{
    void printMyName()
    {
        System.out.println( "PolySub23" );
    }
}

/**
 * 実行用クラス。このクラスを実行してください。
 */
class Poly2Runner
{
    public static void main( String[] args )
    {
        // コマンドライン引数をint型に変換します。
        int i = Integer.parseInt( args[0] );

        // PolySuper2クラスのstaticメソッドを使って
        // インスタンスを取得します。
        PolySuper2 refSuper = PolySuper2.getInstance( i );
        // printMyName()メソッドを呼び出します。
        refSuper.printMyName();
        // 出力結果はコマンドライン引数にどの値を渡したかで変わります。
    }
}
// Poly2Runner.java
/**
 * スーパークラス兼、インスタンス作成クラス。
 * (PolySuperクラスとほぼ同じくラスです)
 */
class PolySuper2
{
	/**
	 * サブクラスのインスタンスを作って、その参照を返します。
	 */
	static PolySuper2 getInstance( int number )
	{
		// 渡された番号によって作るインスタンスを変えます。
		if( number == 1 )
		{
			return new PolySub21();
		}
		else if( number == 2 )
		{
			return new PolySub22();
		}
		else if( number == 3 )
		{
			return new PolySub23();
		}
		else
		{
			return null;
		}
	}
	/**
	 * 自クラスの名前を出力するメソッドです。
	 */
	void printMyName()
	{
		System.out.println( "PolySuper2" );
	}
}
/**
 * PolySuper2クラスから継承したサブクラス x 3。
 * (PolySub*クラスとほぼ同じくラスです)
 */
class PolySub21 extends PolySuper2
{
	void printMyName()
	{
		System.out.println( "PolySub21" );
	}
}
class PolySub22 extends PolySuper2
{
	void printMyName()
	{
		System.out.println( "PolySub22" );
	}
}
class PolySub23 extends PolySuper2
{
	void printMyName()
	{
		System.out.println( "PolySub23" );
	}
}
/**
 * 実行用クラス。このクラスを実行してください。
 */
class Poly2Runner
{
	public static void main( String[] args )
	{
		// コマンドライン引数をint型に変換します。
		int i = Integer.parseInt( args[0] );
		// PolySuper2クラスのstaticメソッドを使って
		// インスタンスを取得します。
		PolySuper2 refSuper = PolySuper2.getInstance( i );
		// printMyName()メソッドを呼び出します。
		refSuper.printMyName();
		// 出力結果はコマンドライン引数にどの値を渡したかで変わります。
	}
}

 前回の例と、スーパークラス・サブクラスはほぼ同じです。

// Poly2Runner.java

/**
 * スーパークラス兼、インスタンス作成クラス。
 * (PolySuperクラスとほぼ同じくラスです)
 */
class PolySuper2
{
    /**
     * サブクラスのインスタンスを作って、その参照を返します。
     */
    static PolySuper2 getInstance( int number )
    {
        // 渡された番号によって作るインスタンスを変えます。
        if( number == 1 )
        {
            return new PolySub21();
        }
        else if( number == 2 )
        {
            return new PolySub22();
        }
        else if( number == 3 )
        {
            return new PolySub23();
        }
        else
        {
            return null;
        }
    }

    /**
     * 自クラスの名前を出力するメソッドです。
     */
    void printMyName()
    {
        System.out.println( "PolySuper2" );
    }
}

/**
 * PolySuper2クラスから継承したサブクラス x 3。
 * (PolySub*クラスとほぼ同じくラスです)
 */
class PolySub21 extends PolySuper2
{
    void printMyName()
    {
        System.out.println( "PolySub21" );
    }
}

class PolySub22 extends PolySuper2
{
    void printMyName()
    {
        System.out.println( "PolySub22" );
    }
}

class PolySub23 extends PolySuper2
{
    void printMyName()
    {
        System.out.println( "PolySub23" );
    }
}
// Poly2Runner.java
/**
 * スーパークラス兼、インスタンス作成クラス。
 * (PolySuperクラスとほぼ同じくラスです)
 */
class PolySuper2
{
	/**
	 * サブクラスのインスタンスを作って、その参照を返します。
	 */
	static PolySuper2 getInstance( int number )
	{
		// 渡された番号によって作るインスタンスを変えます。
		if( number == 1 )
		{
			return new PolySub21();
		}
		else if( number == 2 )
		{
			return new PolySub22();
		}
		else if( number == 3 )
		{
			return new PolySub23();
		}
		else
		{
			return null;
		}
	}
	/**
	 * 自クラスの名前を出力するメソッドです。
	 */
	void printMyName()
	{
		System.out.println( "PolySuper2" );
	}
}
/**
 * PolySuper2クラスから継承したサブクラス x 3。
 * (PolySub*クラスとほぼ同じくラスです)
 */
class PolySub21 extends PolySuper2
{
	void printMyName()
	{
		System.out.println( "PolySub21" );
	}
}
class PolySub22 extends PolySuper2
{
	void printMyName()
	{
		System.out.println( "PolySub22" );
	}
}
class PolySub23 extends PolySuper2
{
	void printMyName()
	{
		System.out.println( "PolySub23" );
	}
}

 単にクラス名が変わっただけで、getInstance()メソッドでしていることや、継承関係はまったく同じです。

 違うのは、これらのクラスを使う側です。
 今回の例では、コマンドライン引数をgetInstance()メソッドに渡しています。

        // コマンドライン引数をint型に変換します。
        int i = Integer.parseInt( args[0] );

        // PolySuper2クラスのstaticメソッドを使って
        // インスタンスを取得します。
        PolySuper2 refSuper = PolySuper2.getInstance( i );
        // printMyName()メソッドを呼び出します。
        refSuper.printMyName();
        // 出力結果はコマンドライン引数にどの値を渡したかで変わります。
		// コマンドライン引数をint型に変換します。
		int i = Integer.parseInt( args[0] );
		// PolySuper2クラスのstaticメソッドを使って
		// インスタンスを取得します。
		PolySuper2 refSuper = PolySuper2.getInstance( i );
		// printMyName()メソッドを呼び出します。
		refSuper.printMyName();
		// 出力結果はコマンドライン引数にどの値を渡したかで変わります。

 コマンドライン引数に渡した値、つまり「java Poly2Runner 1」なら「1」、「java Poly2Runner 3」なら「3」を取得して整数値に変換し、それをgetInstance()メソッドに渡しています。
 そのため、「コマンドライン引数にどの数字を入力するか」によって、作られるインスタンスが変化します。
 1を渡せばPolySub21インスタンスが作られます。
 2を渡せばPolySub22インスタンスが作られます。
 3を渡せばPolySub23インスタンスが作られます。

 そのため、refSuper変数が「どのインスタンスを指すのか」は、実行してみなくちゃわからない」ということです。
 実行して初めてPolySub21、PolySub22、PolySub23いずれかのインスタンスが作られるので、プログラム上からは実際にそのサブクラスのインスタンスが作られるかわからないというわけです。
 そのため、実行結果もわかりません。

 refSuper変数が参照しているインスタンスがどのクラスかということは、実行してみないと分かりません。
 つまりこの例では、「refSuper.printMyName();」というプログラムからは「どのインスタンスのprintMyName()メソッドを呼び出しているのか分からない」ということです。
 そのため、実行結果が「PolySub21」「PolySub22」「PolySub23」のどれになるかは実行してみないと分からないわけです。

 この「どんなクラスが実行されるのか分からない」ことが「ポリモーフィズム」というものです。
 ポリモーフィズムは日本語で「多態性」(たたいせい)、「多相性」(たそうせい)と訳されます。
 PolySuper2クラスの参照型であるrefSuper変数を通してメソッドを呼び出しているのに、実際には「PolySuper2クラスじゃないクラスのように振る舞う」、つまり、いろんな態度を見せる、いろんな顔を見せる――。
 この「実行した時の条件でクラスが変わり結果が変わる」、それがポリモーフィズムというものなのです。

これのどこがいいの?

 このように、ポリモーフィズムは結果が掴みにくい機能です。
 もしかしたら「そのどこがいいのか」分からないかもしれません。なにせ、使っている側からすれば「使う対象を完全に把握できていない」わけですから。
 今回のようにPolySuper2クラスやPolySub21~PolySub23クラスが自分で作ったものならともかく、これらのクラスが提供されたクラスであれば、本当に実行してみないとどのクラスのインスタンスなのかわからないということになります。それは、使う側がすべて管理できない、ということになるわけです。

 でもそれでいいんです
 このポリモーフィズムのように「すべて管理できない」ということが、「オブジェクト指向」っぽい考え方への第一歩なんです。

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