PHPにおける「抽象クラス」「インタフェース」「無名クラス」Web業界で働くためのPHP入門(18)(1/2 ページ)

オープンソースのWeb開発向けスクリプト言語「PHP」の文法を一から学ぶための入門連載。今回は、オブジェクト指向言語の三大特徴の1つであるポリモーフィズムについて、抽象クラスとインタフェースを使って解説します。

» 2018年05月30日 05時00分 公開

 オープンソースのWeb開発向けスクリプト言語「PHP」の文法を一から学ぶための入門連載「Web業界で働くためのPHP入門」。今回はオブジェクト指向言語の三大特徴の1つであるポリモーフィズムについて、抽象クラスとインタフェースを使って解説します。

抽象クラス

 抽象クラスとは、抽象メソッドを含むクラスのことです。抽象メソッドとは処理の書かれていないメソッドのことです。この説明だけではピンとこないと思いますので、具体例で見ていくことにしましょう。

前回のAnimalクラスの欠点

 前回、Animalクラスを作成し、それを継承したクラスとしてDogクラスを作成しました。そのDogクラスには鳴き声を表すcall()メソッドを実装しました。同様の方法でCatやPigなどのクラスを作成できます。その際、CatにもPigにも必ず鳴き声を表すcall()メソッドを実装してほしいとします。

 前回作成したAnimalクラスのままだと、設計書など何かドキュメントに記述するしか方法はありません。しかも、その記述を読み損ねたり読んでいても忘れたりしていると、各子クラスにcall()メソッドの実装忘れが起き、確実にバグとして混入されてきます。

 これは、以下のようにあらかじめAnimalクラスに空のメソッドを記述しておくことで致命的なエラーを避けることはできます。

<?php
class Animal
{
    :
    public function call():void {}
}

 あるいは、このcall()内に何か処理を記述しておき、それをデフォルトの動作としておくことも可能です。しかし、この方法には無理があります。というのは、動物を表すAnimalの鳴き声というのが定まらない以上、call()の処理内容も決まりません。ここでは例えのように「鳴き声」使っていますが、実際の業務システムのクラス設計において、親クラスでは処理内容が決まらないというケースはあります。

処理が書かれていないメソッド

 そこで、登場するのが抽象メソッドです。上述のように、抽象メソッドは処理内容が書かれていないメソッドです。サンプルで見ていくことにしましょう。以下のAbstractAnimalクラスを作成してください。

<?php
abstract class AbstractAnimal  // (1)
{
    //名前のプロパティ
    private $name = "";
 
    //名前のゲッタ
    public function getName(): string
    {
        return $this->name;
    }
 
    //名前のセッタ
    public function setName(string $name): void
    {
        $this->name = $name;
    }
 
    //鳴き声を得るメソッド。
    abstract public function call(): string;  // (2)
}
リスト1 phplesson/chap18/AbstractAnimal.php

 クラス宣言部分の(1)と(2)の行以外は前回のAnimalクラスと同じです。

ここで注目するのは(2)です。(2)では以下のように明らかにメソッドのような記述となっています。

public function call(): string

 メソッド名と引数の有無、戻り値の型といったメソッド定義(これを「メソッドシグネチャ」といいます)は確認できます。一方、メソッドにあるはずの{}(波かっこ)がありません。つまり、処理が記述されていないのです。

 このように、メソッドシグネチャだけで処理が記述されていないメソッドのことを「抽象メソッド」といい、メソッドシグネチャの先頭に必ず「abstract」を付けるのが約束事です。

 また、抽象メソッドを含むクラスのことを「抽象クラス」といい、こちらもクラス定義の先頭に必ず「abstract」を付けるのがお約束事です。リスト1の(1)がその記述です。

 なお抽象クラスのクラス名は、AbstractAnimalのように、通常「Abstract〜」とします。

抽象メソッドは子クラスで必ず実装

 この抽象メソッド(抽象クラス)を利用する利点は、そのクラスを継承した子クラスでは、必ず抽象メソッドをオーバーライドする必要がある、ということです。そこを確認してみましょう。

 まず、AbstractAnimalを正常に継承した子クラスとしてDogクラスを作成します。

<?php
class Dog extends AbstractAnimal  // (1)
{
    //鳴き声を得るメソッド
    public function call(): string
    {
        return "わんわん";
    }
}
リスト2 phplesson/chap18/Dog.php

 (1)の親クラスがAbstractAnimalになっていること以外は、前回作成したDogクラスと同じです。

 次に、同じくAbstractAnimalを継承した子クラスとしてCatクラスを作成しますが、あえて何も記述しないでおきます。

<?php
class Cat extends AbstractAnimal {}
リスト3 phplesson/chap18/Cat.php

 このCatクラスに何も記述がないということは、当然call()メソッドもオーバーライドされていません。この2クラスを利用する実行phpファイルとして以下のcallAnimals.phpを作成し、実行してください。なお、(3)〜(5)はコメントアウトのまま実行してください。

<?php
require_once("AbstractAnimal.php");  // (1)
require_once("Dog.php");  // (2)
 
$pet = new Dog();  // (2)
$pet->setName("ぽち");  // (2)
print($pet->getName()."の鳴き声は".$pet->call());  // (2)
 
// require_once("Cat.php");  // (3)
// $pet2 = new Cat();  // (4)
// $animal = new AbstractAnimal();  // (5)
リスト4 phplesson/chap18/callAnimals.php

 実行結果は以下の通りです。

ぽちの鳴き声はわんわん

 (3)〜(5)のコメントアウト部分を除けば、ほとんど前回作成したcallDog.phpと同じコードです。違うのは(1)のファイルの読み込み部分がAbstractAnimalになっているところだけです((2)は同じコードです)。実行結果も同じです。

 このように、抽象メソッドをちゃんと実装した子クラスを利用する限りは問題なく動作します。

抽象メソッドを実装し忘れるとエラーとなる

 問題となるのは、抽象メソッドが実装されていないクラスを利用する場合です。ここではCatクラスが該当します。(3)でそのCatクラスファイルを読み込んでいます。本来、このようなrequire処理はファイル上部にまとめるのですが、ここは説明の都合上、(3)の位置に記述しています。(4)でCatクラスをnewしています。この2行のコメントアウトを元に戻して再度実行してみてください。実行結果は以下のようにエラーとなります。

ぽちの鳴き声はわんわん
Fatal error: Class Cat contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (AbstractAnimal::call) in C:\xampp\htdocs\phplesson\chap18\Cat.php on line 2

 エラーメッセージは、まさに、抽象メソッドの実装し忘れというものです。

 ここで注目したいのは、(4)をコメントアウトしても同じエラーとなることです。つまり、抽象メソッドを実装し忘れたクラスは、その定義そのものを読み込んだ時点でエラーとなるのです。もし、(3)のrequire処理を従来通りファイル上部で記述していたならば、いち早く実装し忘れに気付けます。

抽象クラスはnewできない

 抽象クラスの最後に、(5)を説明しておきましょう。ここでは、抽象クラスであるAbstractAnimalをnewしています。この1行のコメントアウトを元に戻して実行してみてください(その際、(3)と(4)はコメントアウトしておきましょう)。以下のようなエラーとなります。

ぽちの鳴き声はわんわん
Fatal error: Uncaught Error: Cannot instantiate abstract class AbstractAnimal in C:\xampp\htdocs\phplesson\chap18\callAnimals.php:11 Stack trace: #0 {main} thrown in C:\xampp\htdocs\phplesson\chap18\callAnimals.php on line 11

 エラーメッセージから分かるように、抽象クラスのインスタンスは作成できません。つまり、抽象クラスはnewしてはいけないのです。そもそも、処理内容が書かれていないのが抽象メソッドでしたので、newできないのはすぐに理解できるでしょう。

       1|2 次のページへ

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。