本文へスキップ
BecomeCoder

PHPコース · 第6章 クラスとオブジェクト指向 · レッスン22

継承・インターフェース・trait ― クラスを再利用する

ブラウザで完結

導入

「犬」も「猫」も「動物」という共通点があり、名前を持つ・鳴くといった共通の振る舞いがあります。似たクラスを1つずつゼロから書くのではなく、共通部分を1か所にまとめて再利用したい――これを解決する3つの仕組みが、継承・インターフェース・traitです。

説明

継承(extends は、あるクラスの機能を引き継いだ新しいクラスを作る仕組みです。元のクラスを親クラス、引き継いだクラスを子クラスと呼びます。

<?php
class Animal {
    public string $name;

    public function __construct(string $name) {
        $this->name = $name;
    }

    public function speak(): string {
        return "{$this->name}は鳴きます";
    }
}

class Dog extends Animal {
    // 親のメソッドを上書き(オーバーライド)する
    public function speak(): string {
        return "{$this->name}はワンと鳴きます";
    }
}

$dog = new Dog("ポチ");
echo $dog->speak(), "\n";   // ポチはワンと鳴きます

DogAnimal を継承しているので、__construct() を書かなくても $name を受け取って new Dog("ポチ") ができます。speak()Dog 側でも定義すると、親のものを**上書き(オーバーライド)**できます。親のメソッドをそのまま呼びたいときは parent::speak() と書けます。

classDiagram
    Animal <|-- Dog
    Animal <|-- Cat
    class Animal {
      +string name
      +speak() string
    }
    class Dog {
      +speak() string
    }
    class Cat {
      +speak() string
    }

インターフェース(interface / implements は、「このクラスは必ずこのメソッドを持つ」という**約束(契約)**だけを決める仕組みです。処理の中身は持たず、形だけを定義します。

<?php
interface Payable {
    public function pay(): string;
}

class CreditCard implements Payable {
    public function pay(): string {
        return "クレジットカードで支払いました";
    }
}

class CashPayment implements Payable {
    public function pay(): string {
        return "現金で支払いました";
    }
}

function checkout(Payable $method): void {
    // Payable を実装していれば、中身がどのクラスでも同じように呼び出せる
    echo $method->pay(), "\n";
}

checkout(new CreditCard());
checkout(new CashPayment());

checkout() は「Payable を実装したものなら何でもよい」という前提で書けます。継承は「1つの親」しか持てませんが、implements は複数のインターフェースを同時に実装できます。

trait は、継承関係とは関係なく、複数のクラスにメソッドの実装そのものを混ぜ込む仕組みです。trait で定義し、クラスの中で use トレイト名; します。

<?php
trait Greetable {
    public function greet(): string {
        return "こんにちは、{$this->name}です";
    }
}

class Staff {
    use Greetable;

    public string $name;
    public function __construct(string $name) {
        $this->name = $name;
    }
}

$s = new Staff("田中");
echo $s->greet(), "\n";   // こんにちは、田中です

継承関係にない Staff にも、use Greetable; だけで greet() メソッドが使えるようになります。「継承関係を作るほどではないが、複数クラスで処理を共有したい」ときに便利です。

やってみよう

Animal を継承した Bird クラスを作って speak() をオーバーライドしてみましょう。Payable を実装する3つ目のクラス(例:BankTransfer)を作って checkout() に渡してみるのもよい練習です。

演習

上の Animal クラスを継承した Cat クラスを作り、speak() をオーバーライドして "{名前}はニャーと鳴きます" を返すようにしてください。$cat = new Cat("タマ"); を作り、speak() の結果を表示しましょう。

ヒント1を見る

class Cat extends Animal { public function speak(): string { return "{$this->name}はニャーと鳴きます"; } }

ヒント2を見る

$cat = new Cat("タマ"); echo $cat->speak(), "\n";

実際に動かしてみよう

下のエディタにPHPを書いて「実行」を押すと、ブラウザ内で動く本物のPHP(php-wasm)で出力が表示されます。コードは必ず <?php で書き始めます。本文の例をそのまま試したり、書き換えたりしてみましょう(初回だけPHPの読み込みに少し時間がかかります)。

PHP — ブラウザ内で実行

ブラウザ内で動く本物のPHP(php-wasm / PHP 8.4)を読み込みます(初回のみ読み込みに少し時間がかかります)。
スクロールして表示された時点でも自動で読み込まれます。