#pragma twice

KAB-studio > プログラミング > Javaのオブジェクト指向入門 > 9. 抽象クラスと「抽象的」 > 9.2 抽象クラスの使い道
 
前のページへつぎ

9.2 抽象クラスの使い道

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

 抽象クラスは、一見するとどんな使い道があるかわかりにくいクラスです。
 そこで、一番手っ取り早いメリット「オーバーライドしなきゃいけない!」という点を見てみましょう。

抽象クラスでミスを防ごう

 抽象クラスのメリットを見るために、抽象クラスを使用しなかった場合の失敗例を見てみましょう。

// TypoSubRunner.java

/**
 * 抽象クラス……じゃない、普通のクラス。
 */
class NonAbstractSuper
{
    /**
     * 普通のメソッド。
     */
    void printMyName()
    {
        System.out.println( "NonAbstractSuper" );
    }
}

/**
 * NonAbstractSuperクラスのサブクラス。
 */
class TypoSub extends NonAbstractSuper
{
    /**
     * オーバーライドしている……つもり。
     */
    void printNyName()
    {
        System.out.println( "TypoSub" );
    }
}

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

        // オーバーライドしたメソッドを呼び出します。
        refSuper.printMyName();
        // 実行結果:
        // NonAbstractSuper

        // ……あれ?
    }
}
// TypoSubRunner.java
/**
 * 抽象クラス……じゃない、普通のクラス。
 */
class NonAbstractSuper
{
	/**
	 * 普通のメソッド。
	 */
	void printMyName()
	{
		System.out.println( "NonAbstractSuper" );
	}
}
/**
 * NonAbstractSuperクラスのサブクラス。
 */
class TypoSub extends NonAbstractSuper
{
	/**
	 * オーバーライドしている……つもり。
	 */
	void printNyName()
	{
		System.out.println( "TypoSub" );
	}
}
/**
 * 実行用クラス。このクラスを実行してください。
 */
class TypoSubRunner
{
	public static void main( String[] args )
	{
		// TypoSubクラスのインスタンスを1つ作ります。
		// 参照型変数はNonAbstractSuperクラスです。
		NonAbstractSuper refSuper = new TypoSub();
		// オーバーライドしたメソッドを呼び出します。
		refSuper.printMyName();
		// 実行結果:
		// NonAbstractSuper
		// ……あれ?
	}
}

 まず、NonAbstractSuperクラスがあります。
 これは前ページの抽象クラスに似ていますが、普通のクラスです

/**
 * 抽象クラス……じゃない、普通のクラス。
 */
class NonAbstractSuper
{
    /**
     * 普通のメソッド。
     */
    void printMyName()
    {
        System.out.println( "NonAbstractSuper" );
    }
}
/**
 * 抽象クラス……じゃない、普通のクラス。
 */
class NonAbstractSuper
{
	/**
	 * 普通のメソッド。
	 */
	void printMyName()
	{
		System.out.println( "NonAbstractSuper" );
	}
}

 抽象クラスじゃないので、抽象メソッドは作れません。
 なので、printMyName()メソッドも普通のメソッドです。

 そのサブクラス、TypoSubクラスも用意します。


/**
 * NonAbstractSuperクラスのサブクラス。
 */
class TypoSub extends NonAbstractSuper
{
    /**
     * オーバーライドしている……つもり。
     */
    void printNyName()
    {
        System.out.println( "TypoSub" );
    }
}
/**
 * NonAbstractSuperクラスのサブクラス。
 */
class TypoSub extends NonAbstractSuper
{
	/**
	 * オーバーライドしている……つもり。
	 */
	void printNyName()
	{
		System.out.println( "TypoSub" );
	}
}

 TypoSubクラスは、NonAbstractSuperクラスのサブクラスです。
 このクラスでは、printMyName()メソッドをオーバーライドしています。
 ……そのつもりでした。
 でもオーバーライドできていません
 よく見てください。メソッド名が「printNyName」となっています。スーパークラスのprintMyName()メソッドと、メソッド名が違います!
 こうなるとオーバーライドされません

        // TypoSubクラスのインスタンスを1つ作ります。
        // 参照型変数はNonAbstractSuperクラスです。
        NonAbstractSuper refSuper = new TypoSub();
		// TypoSubクラスのインスタンスを1つ作ります。
		// 参照型変数はNonAbstractSuperクラスです。
		NonAbstractSuper refSuper = new TypoSub();

 このようにインスタンスを作っても、printMyName()メソッドとprintNyName()メソッドは全然関係ないメソッドなので、オーバーライドされず、ひもも付きません。

 当然、スーパークラスのprintMyName()メソッドを呼び出そうとすると、そのままprintMyName()メソッドが呼び出されます。

        // オーバーライドしたメソッドを呼び出します。
        refSuper.printMyName();
        // 実行結果:
        // NonAbstractSuper

        // ……あれ?
		// オーバーライドしたメソッドを呼び出します。
		refSuper.printMyName();
		// 実行結果:
		// NonAbstractSuper
		// ……あれ?

 実行結果から分かるとおり、サブクラスのprintNyName()メソッドは呼ばれるわけがなく、スーパークラスのprintMyName()メソッドが呼び出されます。

 このように、オーバーライドする際にメソッド名を間違えることで、オーバーライドに失敗してしまいます
 その他に、引数を変えた場合にもオーバーライドされません。
 このように、ちょっとしたミスでオーバーライドできず、しかも実行しなければミスしたことに気付きません

 このような問題を回避するためには、抽象クラスを使うことです
 前ページの例のように、クラスを抽象クラスにして、printMyName()メソッドを抽象メソッドにすると、そのメソッドはサブクラスで必ずオーバーライドする必要があります
 もし今回の例のようにオーバーライドできていないとコンパイルエラーになるので一発で分かります。
 このメリットを活用するために、抽象クラスを使ってみましょう。

とは言っても

 ただし、今ここで言ったメリットは「一応のメリット」です。
 たとえば「6.2 3段以上オーバーライドしたら?」のように、数段に渡ってオーバーライドしている場合には、今回のメリットは得られません。たとえHasOneMethodSuper3クラスのprintMyName()メソッドを抽象クラスにしても、SubOfSubクラスでprintMyName()メソッドをタイプミスしてしまったら、それを防ぐことはできません。
 また、単に「オーバーライドのミスをなくしたい」という場合には、「Overrideアノテーション」というのものがあるのでそちらを利用した方がいいでしょう。
 じゃあなんのために……と言われるとちょっと難しいです。
 一応「本来の目的」があるのですが、その目的のためには後で説明する「インタフェース」の方が便利なので、実際に抽象クラスを使う場面は少ないでしょう。
 正直、「まぁこういう機能がある」というくらいに憶えておいて、後述のインタフェースを理解するための道具としてください。

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