| この記事の要点 |
|
基本
class Animal
{
public function __construct(protected string $name) {}
public function speak(): string
{
return "{$this->name} が鳴いた";
}
public function name(): string
{
return $this->name;
}
}
class Dog extends Animal
{
public function speak(): string // 上書き(オーバーライド)
{
return "{$this->name}: ワン";
}
public function fetch(): string // 子だけの機能
{
return "取ってきた";
}
}
$d = new Dog('ポチ');
echo $d->speak(); // ポチ: ワン
echo $d->name(); // ポチ 親のメソッドも使える
echo $d->fetch(); // 取ってきた
var_dump($d instanceof Dog); // true
var_dump($d instanceof Animal); // true 親としても扱える
親の実装を呼ぶ
class Cat extends Animal
{
public function __construct(string $name, private bool $indoor = true)
{
parent::__construct($name); // ← 忘れると $name が未初期化になる
}
public function speak(): string
{
return parent::speak() . '(ニャー)'; // 親の結果に足す
}
}
echo (new Cat('タマ'))->speak(); // タマ が鳴いた(ニャー)
子クラスで __construct を定義しなければ親のものがそのまま使われます。定義した時点で親の初期化は行われなくなるので、parent::__construct() の呼び忘れに注意してください。
可視性(private / protected / public)
class Base
{
public string $a = 'public'; // どこからでも
protected string $b = 'protected'; // 自分と子クラスから
private string $c = 'private'; // 自分のクラスの中だけ
public function show(): void
{
echo $this->a, $this->b, $this->c; // 全部見える
}
}
class Child extends Base
{
public function test(): void
{
echo $this->a; // OK
echo $this->b; // OK
echo $this->c; // Warning: Undefined property Child::$c
}
}
$o = new Child();
echo $o->a; // OK
// echo $o->b; // Error: Cannot access protected property
| クラス内 | 子クラス | 外部 | |
|---|---|---|---|
public | ○ | ○ | ○ |
protected | ○ | ○ | × |
private | ○ | × | × |
継承される前提のクラスでは、子に触ってほしいものを protected、内部実装を private にします。迷ったら狭いほうから始めて、必要になったら広げてください。
final で継承を止める
final class Money { } // このクラスは継承できない
// class Yen extends Money {} // Fatal error: Class Yen cannot extend final class Money
class Service
{
final public function run(): void // このメソッドは上書きできない
{
$this->before();
$this->handle();
$this->after();
}
protected function handle(): void {} // ここだけ子に差し替えさせる
private function before(): void {}
private function after(): void {}
}
処理の流れは固定して一部だけ差し替えさせたいときに final が役立ちます(テンプレートメソッドパターン)。
インターフェース
interface Speaker
{
public function speak(): string; // 実装は書かない
}
interface Walker
{
public function walk(): string;
}
class Robot implements Speaker, Walker // 複数実装できる
{
public function speak(): string { return 'ピー'; }
public function walk(): string { return 'ガシャン'; }
}
function announce(Speaker $s): void // 型として使える
{
echo $s->speak();
}
announce(new Robot());
announce(new Dog('ポチ')); // Dog も implements Speaker していれば渡せる
親クラスは 1 つしか持てませんが、インターフェースはいくつでも実装できます。「これができる」という役割を型で表したいときはインターフェースを使います。
トレイト(実装の使い回し)
trait Timestampable
{
private ?string $createdAt = null;
public function touch(): void
{
$this->createdAt = date('Y-m-d H:i:s');
}
}
trait Loggable
{
public function log(string $msg): void
{
error_log(static::class . ': ' . $msg);
}
}
class Article
{
use Timestampable, Loggable; // 複数まとめて取り込める
}
$a = new Article();
$a->touch();
$a->log('作成');
トレイトは継承関係とは無関係にメソッドを配る仕組みです。同じ名前のメソッドが衝突したときは insteadof と as で解決します。
trait A { public function hello(): string { return 'A'; } }
trait B { public function hello(): string { return 'B'; } }
class C
{
use A, B {
A::hello insteadof B; // A のほうを採用
B::hello as helloB; // B のほうは別名で使う
}
}
echo (new C())->hello(); // A
echo (new C())->helloB(); // B
3 つの使い分け
| 目的 | 数 | 実装を持てるか | |
|---|---|---|---|
extends(継承) | 「〜は〜の一種」を表す | 1 つだけ | 持てる |
implements(インターフェース) | 「〜ができる」を型で表す | いくつでも | 持てない(定数と宣言のみ) |
use(トレイト) | 実装を配る | いくつでも | 持てる(型としては使えない) |
継承より委譲を選ぶ場面
// 良くない継承: 「Stack は ArrayObject の一種」ではない
class Stack extends ArrayObject
{
public function push($v): void { $this[] = $v; }
}
$s = new Stack();
$s->push(1);
$s->asort(); // 親の余計な機能まで公開されてしまう
// 委譲: 必要な操作だけを見せる
class Stack
{
private array $items = [];
public function push(mixed $v): void { $this->items[] = $v; }
public function pop(): mixed { return array_pop($this->items); }
public function count(): int { return count($this->items); }
}
継承すると親の public メソッドがすべて子でも使えるようになります。「便利だから継承する」と、意図しない使われ方を許すことになります。「子は親としても正しく振る舞えるか」を基準に判断してください(ポリモーフィズム(多様性)の具体例)。
関連
子ページ
子ページはありません
人気ページ
- 1 Eclipseで「サーバーに追加または除去できるリソースがありません。」の原因と対処法
- 2 tomcat の起動 / 停止ログと catalina.log・catalina.out の違い
- 3 JavaScript で base URL を取得する方法|window.location.origin
- 4 YouTube Data API v3 エラー一覧|403・400・404 の原因と対処
- 5 Laravel エラー一覧|500/Blade/DB 接続/ルーティングの代表エラー
- 6 3Dグラフィックスとは|モデリング/レンダリング/主要ソフトウェア (Blender / Maya)
- 7 Spring Frameworkのアノテーション一覧
- 8 【Spring】@Valueアノテーションとは
- 9 CATALINA_HOME の確認方法 (Linux / Mac)
- 10 【Spring】@Autowiredアノテーションとは
最近更新/作成されたページ
- プロジェクトをTomcatプロジェクトとして認識させる方法 2026-10-07 22:32:50
- MySQLの1366 Incorrect string value|Laravelの文字コード・絵文字エラー 2026-10-07 21:54:03
- curlの証明書ホスト名不一致|旧エラー51・現行60の確認と対処 2026-10-07 21:54:03
- LaravelのMassAssignmentException|fillableの原因と安全な対処 2026-10-07 21:54:03
- Eclipse で Tomcat の起動ログがコンソールに出ない時の確認手順 2026-10-07 21:54:02
- MySQLにおける中央値(Median)の導き方(バージョン8未満) 2026-10-07 13:49:45
- getInputForward 2026-10-07 13:41:15
- JSONから配列に変換 2026-10-07 13:41:15
- ビューから値をモデルに格納しコントローラーで受け取る方法 2026-10-07 13:23:41
- Laravelのテーブル作成と定義変更|マイグレーション・up/down・注意点 2026-10-07 13:23:41
- NumPy 配列に要素を追加する方法 (append / concatenate) 2026-10-07 13:23:41
- MariaDB・MySQLで現在日時を取得する方法|NOW・タイムゾーン・保存型 2026-10-07 13:13:36
- 【django】テンプレートで定数を使用する方法 2026-10-07 13:10:15
- Spring BootにおけるApplication.propertiesの環境依存設定の分割方法 2026-10-07 12:09:35
- Not supported for DML operations【Springエラー】 2026-10-07 11:09:38