抽象メソッド/抽象クラス

abstract修飾子

クラス定義時の、メソッドを定義する時に、メソッドの中身(処理部)を書かない方法があります。これはメソッドの名前を宣言するだけで、不完全な定義です。この不完全なメソッドを「抽象メソッド」と言い「abstract修飾子」で宣言します。

「抽象メソッド」を含むクラスは、必然的に「抽象クラス」となります。「抽象メソッド」を含まないクラスでも、クラスを「abstract修飾子」で宣言すると、「抽象クラス」となります。「抽象メソッド」を含むクラスは必然的に「抽象クラス」となるのですが、必ずクラスを「abstract修飾子」で宣言しなければいけなくなります。「抽象メソッド」を含むクラスを「アブストラクト」に宣言しないと「E_ERROR」エラーを発します。

「抽象メソッド」は、処理部を備えない不完全なメソッドなので、必ず処理部を実装しなければなりません。派生クラスでオーバーライドして、「抽象メソッド」の処理部を実装します。

「抽象メソッド」をオーバーライドする時は、“メソッドの引数の数を一致”させます。一致させないと、その時点で「E_ERROR」エラーを発します。ただし、オーバーライド時にデフォルト値をセットすれば、引数を追加出来ます。また、「抽象メソッド」ではデフォルト値がセットされていた引数に、オーバーライド時にデフォルト値をセットしないと「E_ERROR」エラーを発します。オーバーライド時に引数の数を増やす事が出来ても減らす事は出来ません。

派生クラスが基底クラスの「抽象メソッド」の処理部を実装しない場合、そのクラスも「抽象クラス」となるので、「abstract修飾子」で宣言する必要があります。

「抽象クラス」は“インスタンスを生成する事を想定しないクラス”です。「抽象クラス」のインスタンスを生成する事は出来ません。

「抽象クラス」を継承したクラスのインスタンスを生成するには、そのクラスが「抽象メソッド」を全てオーバーライドして、その処理部を実装する必要があります。要するに「抽象クラス」で無くすればいいのです。

また、「抽象メソッド」を含まないクラスをわざわざ「abstract修飾子」で「抽象クラス」にすることで、そのクラスはインスタンスの生成を想定していない(そのクラスの派生クラスが定義されることを想定する)と示す事が出来ます。

これまた論よりコード。以下のサンプルと結果をご覧下さい。

<pre>
<?php
    #「抽象メソッド」を含むのでクラスを「アブストラクト」宣言する
    abstract class ANIMAL{
        private $weight;
        
        #通常のメソッドの定義
        protected function __construct($weight){
            $this->weight = $weight;
        }
        final public function get_weight(){
            return $this->weight;
        }
        
        #「抽象メソッド」
        abstract public function action();
        
        #メソッドを「アブストラクト」宣言したのに普通に定義している
        /*
        abstract public function test_method(){
            //処理
        }
        */
    }
    
    #「アブストラクト」宣言したクラスは「抽象クラス」になる
    abstract class HODE{
        //
    }
    
    #「抽象メソッド」を含むのにクラスを「アブストラクト」宣言していない
    /*
    class APETOPE{
        abstract public function test_method();
    }
    class SYANE extends ANIMAL{
        //(「抽象メソッド」の処理部を実装していないので「抽象クラス」のまま)
    }
    */
    
    #「抽象クラス」を継承し、「抽象メソッド」の処理部を実装
    class CAT extends ANIMAL{
        public function __construct($weight){
            parent::__construct($weight);
        }
        public function action(){
            //猫の営み(?)
        }
    }
    class HUMAN extends ANIMAL{
        public function __construct($weight){
            parent::__construct($weight);
        }
        public function action(){
            //人の営み(?)
        }
    }
    
    #「抽象クラス」のインスタンス生成を試みる
    /*
    new ANIMAL(10);
    new HODE;
    */
    
    #インスタンス生成
    new CAT(6);
    new HUMAN(60);
?>

<hr />

<?php
    abstract class APETOPE{
        public function normal_method($arg){
            //
        }
        abstract public function abstract_method1($arg);
        abstract public function abstract_method2($arg = "APETOPE");
    }
    
    class NDA extends APETOPE{
        public function normal_method(){
            //
        }
        /*
        public function abstract_method1(){
            //
        }
        public function abstract_method2(){
            //
        }
        public function abstract_method1($arg = "HODENASU"){
            //
        }
        public function abstract_method2($arg){
            //
        }
        public function abstract_method1($arg1, $arg2){
            //
        }
        */
        public function abstract_method1($hode, $nasu = "HODENASU"){
            //
        }
        public function abstract_method2($arr = array(), $nasu = "NASU"){
            //
        }
    }
    
    class GASU extends NDA{
        /*
        public function abstract_method1(){
            //
        }
        */
        public function abstract_method1($syane){
            //
        }
    }
?>
</pre>
Fatal error:「abstract」宣言したメソッドに処理部が記述されている)
(Fatal error:「抽象メソッド」を含むクラスを「abstract」宣言していない)
(Fatal error:「抽象クラス」のインスタンスは生成出来ない)

Fatal error:「抽象メソッド」と引数の形態が一致していない) (Fatal error:「抽象メソッド」と引数の形態が一致していない)

例によって、コメントアウトしている所で「E_ERROR」エラーが発生します。

定義部を見ると、「abstract public function action($arg = "");」となっています。「{}(波括弧)」で囲まれた処理部が無く、最後が「;(セミコロン)」で終っています。「アブストラクト」宣言したメソッドは必ずこの様に記述します。

「アブストラクト」宣言したクラスは「抽象メソッド」を含まなくても「抽象クラス」となります。「抽象メソッド」を含むクラスは必然的に「抽象クラス」になるので、必ず「アブストラクト」宣言します。「抽象メソッド」を含むクラスを継承したクラスが、その処理部を実装しない場合は「抽象クラス」のままです。

「抽象クラス」のインスタンスを生成する事は出来ません。

通常のメソッドの場合とは違って、「抽象メソッド」をオーバーライドする場合は、引数の形態を一致させる必要があります。ただし、デフォルト値をセットした引数を追加出来、また、引数名とデフォルト値が異なるのもOKです。

「抽象メソッド」を派生クラスでオーバーライドして(処理部を実装して)、また更にそのクラスの派生クラスでオーバーライドする場合も、元の「抽象メソッド」と、引数の形態を一致させなくてはなりません。

そのクラスはあるメソッドを必ず備える(呼び出し方法(引数の形態)も決まっている)が、現時点では処理方法を確定したくない(具体的にどう処理するかは派生クラスに後回し)という場合や派生クラスによってその処理方法が異なるという場合には、基底クラスで「抽象メソッド」を定義しておいて、派生クラスで各々の処理部を実装するようにします。また、自身のインスタンスが生成される事を想定せず、必ずその派生クラスが定義されることを示すために、「抽象メソッド」を含まないクラスを「アブストラクト」宣言して「抽象クラス」にし、インスタンスの生成が出来ないように出来ます(変な日本語・・・まいいか今更)。

作成日:2005年01月05日 最終更新日:2005年01月05日
【印刷モード風モード で表示】