PHPの抽象クラス(abstract class)は、直接インスタンス化できないクラスで、子クラスに実装を強制するための設計の枠組みです。abstract キーワードを付けて宣言し、抽象メソッド(中身のないメソッド)を持つことができます。
抽象クラスは「子クラスはこのメソッドを必ず実装しなさい」という契約を定めるもので、共通の処理と個別の処理を分離する設計パターンの基盤になります。
基本的な使い方
PHP
<?php
abstract class Shape {
// 抽象メソッド:子クラスで必ず実装する
abstract public function area(): float;
abstract public function perimeter(): float;
// 通常のメソッド:そのまま継承される
public function describe(): string {
return "面積: " . round($this->area(), 2) . "、周囲長: " . round($this->perimeter(), 2);
}
}
class Circle extends Shape {
public function __construct(private float $radius) {}
public function area(): float {
return M_PI * $this->radius ** 2;
}
public function perimeter(): float {
return 2 * M_PI * $this->radius;
}
}
class Rectangle extends Shape {
public function __construct(
private float $width,
private float $height
) {}
public function area(): float {
return $this->width * $this->height;
}
public function perimeter(): float {
return 2 * ($this->width + $this->height);
}
}
// $shape = new Shape(); // Error: 抽象クラスはインスタンス化できない
$circle = new Circle(5);
$rect = new Rectangle(4, 6);
echo "円: " . $circle->describe() . "\n";
echo "長方形: " . $rect->describe() . "\n";
実行結果
円: 面積: 78.54、周囲長: 31.42
長方形: 面積: 24、周囲長: 20
抽象クラス Shape は area() と perimeter() の実装を子クラスに強制します。describe() は共通処理として提供されます。
抽象クラスの特徴
PHP
<?php
abstract class Animal {
// コンストラクタも持てる
public function __construct(protected string $name) {}
// 抽象メソッド
abstract public function speak(): string;
// 通常のメソッド
public function introduce(): string {
return "{$this->name}は「{$this->speak()}」と鳴きます";
}
// 静的メソッドも持てる
public static function createRandom(): string {
$types = ["Dog", "Cat"];
return $types[array_rand($types)];
}
}
class Dog extends Animal {
public function speak(): string {
return "ワンワン";
}
}
class Cat extends Animal {
public function speak(): string {
return "ニャー";
}
}
$dog = new Dog("ポチ");
$cat = new Cat("タマ");
echo $dog->introduce() . "\n";
echo $cat->introduce() . "\n";
実行結果
ポチは「ワンワン」と鳴きます
タマは「ニャー」と鳴きます
実用的な例
PHP
<?php
abstract class DataExporter {
protected array $data;
public function __construct(array $data) {
$this->data = $data;
}
// テンプレートメソッド
final public function export(): string {
$header = $this->formatHeader();
$body = $this->formatBody();
return $header . "\n" . $body;
}
abstract protected function formatHeader(): string;
abstract protected function formatBody(): string;
}
class CsvExporter extends DataExporter {
protected function formatHeader(): string {
if (empty($this->data)) return "";
return implode(",", array_keys($this->data[0]));
}
protected function formatBody(): string {
$lines = [];
foreach ($this->data as $row) {
$lines[] = implode(",", array_values($row));
}
return implode("\n", $lines);
}
}
class JsonExporter extends DataExporter {
protected function formatHeader(): string {
return '{"data": [';
}
protected function formatBody(): string {
$items = [];
foreach ($this->data as $row) {
$items[] = json_encode($row, JSON_UNESCAPED_UNICODE);
}
return implode(",\n", $items) . "\n]}";
}
}
$data = [
["name" => "田中", "age" => 30],
["name" => "佐藤", "age" => 25],
];
echo "--- CSV ---\n";
echo (new CsvExporter($data))->export() . "\n\n";
echo "--- JSON ---\n";
echo (new JsonExporter($data))->export() . "\n";
実行結果
--- CSV ---
name,age
田中,30
佐藤,25
--- JSON ---
{"data": [
{"name":"田中","age":30},
{"name":"佐藤","age":25}
]}
抽象クラスとインターフェースの違い
抽象クラスは通常のメソッド(実装付き)も持てますが、インターフェースは基本的にメソッドのシグネチャのみです。抽象クラスは単一継承のみ、インターフェースは複数実装可能です。「共通処理+個別処理」なら抽象クラス、「契約の定義のみ」ならインターフェースを使い分けましょう。
注意
子クラスが抽象メソッドをすべて実装しないと、その子クラスも抽象クラスとして宣言する必要があります。また、抽象メソッドのアクセス修飾子は子クラスで同じかより緩い(publicなど)ものにする必要があります。
まとめ
abstract classは直接インスタンス化できない設計の枠組み- 抽象メソッドは子クラスで必ず実装しなければならない
- 通常のメソッドやコンストラクタも定義でき、子クラスに共通処理を提供できる
- テンプレートメソッドパターンとの相性が良い
- 共通処理があるなら抽象クラス、契約のみならインターフェースを使う