概要
final 修飾子は 「これ以上の拡張を禁止する」 ための宣言で、クラスに付ければ extends できなくなり、メソッドに付ければそのメソッドのオーバーライドができなくなる。final class Config { ... } と書けば誰も Config を継承して挙動を差し替えられず、final public function save() と書けば子クラスが save() を上書きすることを防げる。「ここは変えてほしくない」と意図を コードで明示 できるのが final の役割になる。
なぜ拡張を禁止したいのかというと、Liskov 置換原則 (LSP) が守られない継承を未然に防ぐためだ。継承は強力だが、子クラスが親の前提を壊すように書かれると「親型として扱える」性質が崩れ、呼び出し側で予期しない動作になる。クラスを公開する側が「このクラスは継承を想定していない」と判断したなら、final class にしておくことで誤った継承を防げる。
実務的には すべての公開クラスを最初から final にして、必要になったときだけ外す という方針もよく採られる。クラスを継承可能にすると、後で内部実装を変えにくくなる (子クラスが内部メソッドに依存しているかもしれない) ので、迷ったら拡張を閉じる側に倒すという考え方だ。継承よりもインターフェース + コンポジションで拡張点を作る、というモダンな設計とも相性がよい。
学習目標
final classは 継承を禁止 するfinal functionはそのメソッドの オーバーライドを禁止 する- 「これ以上拡張させない」と意図を明示できる
ドリル
| no | 内容 |
|---|---|
| 01 | final class を作って単一インスタンスを生成・出力する |
📝 演習: ドリル 01 — final class でインスタンスを作る
問題
Logger クラスを final を付けて定義してください。
- メソッド
log(string $msg)は"[LOG] {msg}"を 1 行出力する
Logger のインスタンスを作って、log("started") を呼んでください。
期待される出力:
[LOG] startedメモ
final classを継承しようとすると Fatal error になります- このドリルでは「
finalを付けても インスタンス化と通常メソッド呼び出しは普通にできる」ことを確認します
ヒント
final class Logger { ... }$l = new Logger();$l->log("started");