extends / implements / abstract / interface
| 対応: | PHP 5(2004) |
|---|
クラスの継承、インターフェースの実装、抽象クラスの定義を行うための構文です。コードの再利用性と拡張性を高めます。
構文
// 親クラスを継承する
class 子クラス extends 親クラス { }
// インターフェースを定義する
interface インターフェース名 {
public function method(): void;
}
// インターフェースを実装する
class クラス名 implements インターフェース名 { }
// 抽象クラスを定義する
abstract class 抽象クラス名 {
abstract public function method(): void;
}
構文一覧
| 構文 | 概要 |
|---|---|
| extends | 親クラスを継承して子クラスを定義します。PHPの継承は単一継承のみで、1つのクラスしか継承できません。 |
| implements | インターフェースを実装します。カンマ区切りで複数のインターフェースを同時に実装できます。 |
| interface | メソッドの仕様だけを定義する型です。実装は含まず、クラスが必ず実装すべきメソッドを規定します。 |
| abstract | 抽象クラスや抽象メソッドを定義します。抽象クラスはインスタンス化できず、子クラスで抽象メソッドを実装する必要があります。 |
| parent:: | 親クラスのメソッドやコンストラクターを呼び出す際に使用します。メソッドのオーバーライド時に親の処理も実行したい場合に使います。 |
サンプルコード
sample_extends.php
<?php
// 基本的な継承の例
class Fighter {
public function __construct(
protected string $name
) {}
public function battle(): string {
return "{$this->name}が身構えている。";
}
}
class KofFighter extends Fighter {
public function battle(): string {
return "{$this->name}が動き出した!"; // メソッドをオーバーライドする
}
public function special(): string {
return "{$this->name}の鬼焼き!";
}
}
$fighter = new KofFighter("八神庵");
echo $fighter->battle(); // 八神庵が動き出した!
echo $fighter->special(); // 子クラス独自のメソッドも使える
// parent:: で親のコンストラクターを呼び出す例
class User {
public function __construct(
protected string $name,
protected int $age
) {}
}
class Admin extends User {
public function __construct(
string $name,
int $age,
private string $role
) {
parent::__construct($name, $age); // 親のコンストラクターを実行する
}
}
// インターフェースの定義と実装
interface Loggable {
public function to_log(): string;
}
interface Cacheable {
public function cache_key(): string;
}
// 複数のインターフェースを同時に実装できる
class Article implements Loggable, Cacheable {
public function __construct(
private int $id,
private string $title
) {}
public function to_log(): string {
return "[Article:{$this->id}] {$this->title}";
}
public function cache_key(): string {
return "article_{$this->id}";
}
}
$article = new Article(1, "PHPの基本");
echo $article->to_log();
echo $article->cache_key();
// 抽象クラスの例
abstract class Shape {
abstract public function area(): float;
public function describe(): string {
return "面積は " . $this->area() . " です。";
}
}
class Circle extends Shape {
public function __construct(
private float $radius
) {}
public function area(): float {
return pi() * $this->radius ** 2;
}
}
class Rectangle extends Shape {
public function __construct(
private float $width,
private float $height
) {}
public function area(): float {
return $this->width * $this->height;
}
}
$circle = new Circle(5);
echo $circle->describe();
$rect = new Rectangle(4, 6);
echo $rect->area();
php sample_extends.php 八神庵が動き出した!八神庵の鬼焼き![Article:1] PHPの基本article_1面積は 78.539816339745 です。24
概要
『extends』による継承は、既存のクラスの機能を引き継ぎつつ新しい機能を追加・変更するための仕組みです。PHPの継承は単一継承のみで、1つのクラスは1つの親クラスしか持てません。複数のクラスの機能を組み合わせたい場合は、インターフェースや『trait』を使うことができます。
インターフェースは「このメソッドを必ず実装する」という契約を定義するものです。クラスの実装に依存せずにメソッドの存在を保証できるため、大規模なプログラムでの型安全性の向上に役立ちます。抽象クラスはインターフェースと具象クラスの中間的な存在で、一部のメソッドだけ実装を強制し、共通の処理は親クラスにまとめることができます。
クラスの基本については『class』、アクセス修飾子については『public / private / protected』を参照してください。
PHPは単一継承のみ(複数の親クラスは持てない)
PHPでは1つのクラスが複数の親クラスを継承することはできません。『class A extends B, C {}』のように書くとエラーになります。複数のクラスの機能を組み合わせたい場合はtraitを、型の契約を定義したい場合はinterfaceを使います。
extends_trait.php
<?php
// 複数継承はエラーになる(NG)
// class KofChampion extends Fighter, KingOfFighters { } // Parse error
// trait で複数のクラスの機能を組み合わせる(OK)
trait FlameAttack {
public function flame(): string {
return "{$this->name}:鬼焼き!";
}
}
trait DarkPower {
public function dark(): string {
return "{$this->name}:八咫烏!";
}
}
class KofFighter {
use FlameAttack, DarkPower;
public function __construct(protected string $name) {}
}
$iori = new KofFighter("八神庵");
echo $iori->flame();
echo $iori->dark();
php extends_trait.php 八神庵:鬼焼き!八神庵:八咫烏!
abstractメソッドがある場合クラスもabstractにする必要がある
抽象メソッドを含むクラスにabstractキーワードを付け忘れると致命的エラーになります。abstractメソッドを1つでも持つクラスは必ずabstract宣言が必要です。
extends_abstract.php
<?php
// abstractを付け忘れるとFatal Errorになる(NG)
// class Fighter {
// abstract public function special(): string; // Fatal error
// }
// クラスにもabstractを付ける(OK)
abstract class Fighter {
abstract public function special(): string;
public function battle(): string {
return $this->special() . "で攻撃!";
}
}
class KyoFighter extends Fighter {
public function __construct(private string $name) {}
public function special(): string {
return "{$this->name}の百式・鬼焼き";
}
}
$kyo = new KyoFighter("草薙京");
echo $kyo->battle();
php extends_abstract.php 草薙京の百式・鬼焼きで攻撃!
parent::__construct()を呼ばないと親の初期化が実行されない
子クラスで__construct()を定義した場合、明示的にparent::__construct()を呼ばないと親クラスのコンストラクタは実行されません。親クラスのプロパティ初期化が行われないため、予期しないエラーの原因になります。
extends_construct.php
<?php
class Fighter {
public function __construct(
protected string $name,
protected string $team
) {
echo "{$this->name}({$this->team})を初期化。\n";
}
}
// parent::__construct() を呼ばないパターン(NG)
class BadFighter extends Fighter {
public function __construct(string $name, string $team, private int $power) {
// parent::__construct() を呼んでいないので $name と $team が初期化されない
}
}
// parent::__construct() を呼ぶパターン(OK)
class GoodFighter extends Fighter {
public function __construct(string $name, string $team, private int $power) {
parent::__construct($name, $team); // 親の初期化を実行する
}
}
$terry = new GoodFighter("テリー・ボガード", "チームバスターウルフ", 95);
echo $terry->name;
php extends_construct.php テリー・ボガード(チームバスターウルフ)を初期化。 テリー・ボガード
実践パターン
テンプレートメソッドパターン
親クラスで処理の骨格(テンプレート)を定義し、具体的な処理を子クラスで実装するパターンです。共通の流れを親クラスにまとめることで、コードの重複を防ぎます。
extends_template.php
<?php
abstract class KofCharacter {
public function __construct(protected string $name) {}
// テンプレートメソッド:戦闘の流れを定義する
final public function fight(): void {
$this->enter();
echo $this->special() . "\n";
$this->win();
}
protected function enter(): void {
echo "{$this->name}が登場!\n";
}
abstract protected function special(): string;
protected function win(): void {
echo "{$this->name}の勝利!\n";
}
}
class IoriFighter extends KofCharacter {
protected function special(): string {
return "{$this->name}:八咫烏!";
}
}
class MaryFighter extends KofCharacter {
protected function special(): string {
return "{$this->name}:ダイナマイトスウィング!";
}
}
$iori = new IoriFighter("八神庵");
$iori->fight();
$mary = new MaryFighter("ブルー・マリー");
$mary->fight();
php extends_template.php 八神庵が登場! 八神庵:八咫烏! 八神庵の勝利! ブルー・マリーが登場! ブルー・マリー:ダイナマイトスウィング! ブルー・マリーの勝利!
ポリモーフィズムの活用
親クラスの型として子クラスのオブジェクトを扱い、同じメソッド呼び出しで異なる動作を実現するパターンです。
extends_poly.php
<?php
abstract class Fighter {
abstract public function battle_cry(): string;
}
class IoriFighter extends Fighter {
public function battle_cry(): string { return "消えろ…"; }
}
class KyoFighter extends Fighter {
public function battle_cry(): string { return "燃え尽きろ!"; }
}
class KingFighter extends Fighter {
public function battle_cry(): string { return "よし、行くわよ!"; }
}
// Fighter型の配列として扱う
$fighters = [new IoriFighter(), new KyoFighter(), new KingFighter()];
foreach ($fighters as $fighter) {
echo $fighter->battle_cry() . "\n"; // 各クラスの実装が呼ばれる
}
php extends_poly.php 消えろ… 燃え尽きろ! よし、行くわよ!
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。