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

クラスとオブジェクト

抽象クラスとは?

 状態:未整理  閲覧数:6,911  投稿日:2010-05-24  更新日:2013-12-26  
・「インスタンスを生成することができないクラス」=「必ず継承して利用するクラス」
ex)$product = new Product(); // エラーとなる

定義の仕方
・abstract class クラス名
・「abstract アクセス修飾子」を付与することで、そのクラスは抽象クラスとして定義される


抽象メソッドとは?


・処理内容を持たずに名前だけ定義されたメソッドのこと。抽象クラスにて、「abstract修飾子」で宣言する

抽象メソッド書式
・abstract アクセス修飾子 function メソッド名();
・処理内容を記述することができないため、「{}(波括弧)」で囲まれた処理部がなく、最後が「;(セミコロン)」
 
abstract class Product{
   abstract public function setPrice();// 抽象メソッド
}


▼結果(定義しただけなので、出力結果なし)

  /demo/abstract_01.html






抽象メソッドは、継承先のクラスで必ずオーバーライドする必要がある

「抽象クラスの実装を行わずに、Fatal Errorが発生する」例

「Fatal error: Class FoodProduct contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (Product::setPrice)」
 
abstract class Product{//抽象クラス
   abstract public function setPrice();// 抽象メソッド
}

//class FoodProduct extends Product{抽象クラスを継承したFoodProductクラスでは、setPriceメソッドを必ず実装する必要あり
//}


▼結果(コメントアウトしているので、エラー表示なし)

  /demo/abstract_02.html






・抽象クラスには、抽象メソッドのみではなく通常のクラスのように普通のメソッドやフィールドの定義も可能

実際に抽象クラスを使った例

(文字列に対して文字列を結合するより、文字列に対して数値を結合する方が時間がかかる。数値を結合しようとすると、内部で数値を文字列に変換するという処理が入るため、その分遅くなる)
 
abstract class TimeMeasurer{//抽象クラス
   abstract protected function process();//抽象メソッド
   
   public function exec(){
       $startTime = $this->getMicrotime();//ミリ秒単位の時間を取得
       $this->process();//processメソッドを参照
       $endTime = $this->getMicrotime();//ミリ秒単位の時間を取得
       $procSecs = $endTime - $startTime;//その差を求める
       echo sprintf('処理所要時間は%s秒でした。', $procSecs);//sprintf → フォーマット文字列 format  に基づき生成された文字列を返す
   }
   
   private function getMicrotime(){
       $splitedMt = explode(' ', microtime());//文字列を文字列により分割し、文字列の配列を返す
       return $splitedMt[0] + $splitedMt[1];
   }
}

class TestClass1 extends TimeMeasurer{//抽象クラスを継承
   protected function process(){//processメソッドをオーバーライドして処理を実装
       for ($i = 0; $i < 1000000; $i++) {
           $str .= 1;//文字列結合の際、数値を結合
       }
   }
}

class TestClass2 extends TimeMeasurer{//抽象クラスを継承
   protected function process(){//processメソッドをオーバーライドして処理を実装
       for ($i = 0; $i < 1000000; $i++) {
           $str .= '1';//文字列結合の際、文字列を結合
       }
   }
}

$test1 = new TestClass1();
$test1->exec();

$test2 = new TestClass2();
$test2->exec();


▼結果

  /demo/abstract_03.html











・「abstract」宣言したクラスは「抽象メソッド」を含まなくても「抽象クラス」になる。

・「抽象メソッド」を含むクラスは必然的に「抽象クラス」になるので、「abstract」宣言しないとエラーとなる。

・「抽象メソッド」を含むクラスを継承した派生クラスが、その処理部を同じく実装しない場合は、その「抽象クラス」のまま。



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



・「抽象メソッド」を派生クラスでオーバーライドして(処理部を実装して)、また更にそのクラスの派生クラスでオーバーライドする場合も、元の「抽象メソッド」と、引数の形態を一致させなくてはならない。
 
   #抽象クラス(=「抽象メソッド」を含むため、クラスを「abstract」宣言」
   abstract class AIRPORT{
       private $p1;
       
       #通常のメソッド定義
       protected function __construct($p1){
           $this->p1 = $p1;
       }
       final public function get_p1(){
           return $this->p1;
       }
       
       #「抽象メソッド」
       abstract public function action();
       
       #メソッドを「abstract」宣言しているのに、処理部を記述{//処理} ⇒ Fatal error: Abstract function AIRPORT::test_method() cannot contain body
      /*
       abstract public function test_method(){
           //処理
       }
       */
   }

   abstract class A1{//抽象クラス = 「abstract」宣言したクラス
       //
   }
   
   #「抽象メソッド」を含むクラスを「abstract」宣言していない
   #  ⇒ Fatal error: Class C1 contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (C1::test_method)
   /*
   class C1{
       abstract public function test_method();//「抽象メソッド」
   }

   #「抽象メソッド」を含むクラスを「abstract」宣言していない
   #  ⇒ Fatal error: Class TOYAMA contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (AIRPORT::action)
   /*
   class TOYAMA extends AIRPORT{
       //(「抽象メソッド」の処理部を実装していないので、派生クラスも基底クラスと同じ「抽象クラス」のまま)
   }
  */
   
   #「抽象クラス」を継承し、「抽象メソッド」の処理部を記述
   class NARITA extends AIRPORT{
       public function __construct($p1){
           parent::__construct($p1);
       }
       public function action(){
           //処理
       }
   }
   class HANEDA extends AIRPORT{
       public function __construct($p1){
           parent::__construct($p1);
       }
       public function action(){
           //
       }
   }
   
   #「抽象クラス」のインスタンス生成を試みると、エラー。「抽象クラス」のインスタンスを生成することはできない
   /*
   new AIRPORT(10);//Fatal error:  Cannot instantiate abstract class AIRPORT
   new A1;//Fatal error:  Cannot instantiate abstract class A1
   */
   
   #インスタンス生成
   new NARITA(6);
   new HANEDA(60);
?>




<?php
   abstract class C1{//抽象クラス
       public function m1($arg){}
       abstract public function am1($arg);//抽象メソッド。オーバーライドした処理部を記述する場合の引数は、「引数1」+「デフォルト値をセットした引数」いくつでも
       abstract public function am2($arg = "C1");//抽象メソッド。オーバーライドした処理部を記述する場合の引数は、「デフォルト値をセットした引数」のみ、いくつでも追加できる
   }
   
   class C2 extends C1{
       public function m1(){}

   #オーバーライドされた「抽象メソッド」の引数形態が一致していないために引き起こされるエラー。
       //public function am1(){}//引数の数が0だから、エラー。Fatal error: Declaration of C2::am1() must be compatible with that of C1::am1()

       //public function am1($arg = "引数1"){ }//引数が「デフォルト値をセットした引数」のみだからエラー。Fatal error: Declaration of C2::am1() must be compatible with that of C1::am1()

       //public function am1($arg1, $arg2){}//引数の数が2だからエラー。Fatal error: Declaration of C2::am1() must be compatible with that of C1::am1()
   
       public function am1($arg1, $arg2 = "引数1"){}//デフォルト値をセットした引数を追加できる

      // public function am2(){}//Fatal error: Declaration of C2::am2() must be compatible with that of C1::am2()

      // public function am2($arg){}//Fatal error: Declaration of C2::am2() must be compatible with that of C1::am2()
     
    // public function am2($arr = array()){} //○
 
    // public function am2($arg2 = "引数2"){} //○
 
      //public function am2($arr = array(), $arg2 = "引数2"){} //○

      //オーバーライドした処理部を記述する場合の引数は、「デフォルト値をセットした引数」のみ、いくつでも追加できる
      public function am2($arr = array(), $arg2 = "引数2", $arg3 = "引数3"){}
   }
   
   class C3 extends C2{

       //public function am1(){}//Fatal error: Cannot redeclare C3::am1()
        public function am1($arg7){}
   }


▼結果

  /demo/abstract.html


final修飾子

インターフェイス

コメント投稿(ログインが必要)



週間人気ページランキング / 6-7 → 6-13
順位 ページタイトル抜粋 アクセス数
1 PHPで定数を定義する方法は2種類ある / 配列定数の定義 9
2 コード例 … 「例外処理」はネストすることができる 5
2 ブラウザを閉じたらセッションデータはどうなるの? | セッション 5
3 Parse error: syntax error, unexpected 'public' (T_PUBLIC) | Parse error(エラーメッセージ) 4
4 ガベージコレクション | 機能 3
4 Warning: Unexpected character in input: '\' (ASCII=92) state=1 in ○○.php on line △△ | Warning(エラーメッセージ) 3
4 curl で Cookie を使用する 3
4 PHPにおけるメソッドのオーバーライドについて /「引数の数や型は、親クラスのメソッドと完全に一致していなければなりません。」とは具体的にどういう意味ですか? 3
4 Catchable fatal error: Object of class DateTime could not be converted to string | Fatal error(エラーメッセージ) 3
4 Fatal error: Uncaught PDOException: SQLSTATE[HY000] [2002] Connection refused | Fatal error(エラーメッセージ) 3
4 Warning: include() [function.include]: Failed opening '**.php' for inclusion (in | Warning(エラーメッセージ) 3
4 ( ! ) Deprecated: implode(): Passing glue string after array is deprecated. Swap the parameters in ★★★ headless-chromium-php/vendor/wrench/wrench/lib/Wrench/Protocol/Protocol.php | エラーメッセージ 3
5 Fatal error: Wrong parameters for Exception([string $exception [, long $code ]]) | Fatal error(エラーメッセージ) 2
5 セッション管理が必要な理由は、HTTPプロトコルには状態を保持する機能がないため | セッション 2
5 Fatal error: require_once(): Failed opening required 'PEAR.php' | Fatal error(エラーメッセージ) 2
5 Fatal error: Uncaught RuntimeException: SplFileObject::__construct(): failed to open stream: Permission denied in | Fatal error(エラーメッセージ) 2
5 ( ! ) Fatal error: Uncaught PDOException: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'カラム名' cannot be null | Fatal error(エラーメッセージ) 2
5 session_start() | セッション 2
5 @ | 演算子 2
5 Parse error: syntax error, unexpected T_REQUIRE_ONCE, expecting T_FUNCTION | Parse error(エラーメッセージ) 2
2024/6/14 1:01 更新