PHPのfinalキーワードについての考察
こんにちは、株式会社Gizumoでエンジニアをしている日野です。
今回は弊社のバックエンド開発時のメイン言語としているPHP
で使えるfinal
キーワードについて使い所を見直してみました。
前提・対象とする読者
この記事は以下を前提として読み進めてください。
- サンプルコードは
PHP
で記述 - 筆者が動作確認を行った環境は
PHP 8.1.11
また、対象とする読者は以下のような方を想定しています。
PHP
を使ったことがある方final
キーワードの使い所に悩んでいる方
finalキーワードとは
final
キーワードは、拡張(クラスの継承やメソッドのオーバーライド)をさせないよう制限するための修飾子です。final
キーワードを付与できる対象は、クラス / メソッド / 定数となっています。※定数についてはPHP 8.1.0以降
実際のコードを例に挙動を確認してみたいと思います。
クラスに対してfinalキーワード付与
クラスに対してfinal
キーワードを付与した場合、クラスの継承を禁止することができます。
<?php
declare(strict_types=1);
final class Sample
{
public function hello(): void
{
echo 'hello! from Sample';
}
}
class AnotherSample extends Sample
{
public function hello(): void
{
echo 'hello! from AnotherSample';
}
}
$anotherSample = new AnotherSample();
$anotherSample->hello();
このコードを実行すると以下のようなエラーになり、AnotherSampleクラスはfinalクラスであるSampleクラスを継承できないことが確認できます。
PHP Fatal error: Class AnotherSample cannot extend final class Sample in /Path/to/sample.php on line 12
メソッドに対してfinalキーワード付与
続いてメソッドに対して個別にfinal
キーワードを付与してみます。
そうすることで、final
キーワードを付与したメソッドのオーバーライドを禁止することができます。
<?php
declare(strict_types=1);
class Sample
{
final public function hello(): void
{
echo 'hello! from Sample';
}
}
class AnotherSample extends Sample
{
public function hello(): void
{
echo 'hello! from AnotherSample';
}
}
$anotherSample = new AnotherSample();
$anotherSample->hello();
コードの実行結果は以下のようになります。finalメソッドであるSampleクラスのhelloメソッドをオーバーライドできないことが確認できます。
PHP Fatal error: Cannot override final method Sample::hello() in /Path/to/sample.php on line 14
定数に対してfinalキーワード付与
最後に定数に対して個別にfinal
キーワードを付与してみます。
この指定により、final
メソッドを付与した定数のオーバーライドを禁止することができます。
注意点として、先述の通りPHP 8.1.0
以降でのみ使用可能になります。
<?php
declare(strict_types=1);
class Sample
{
final public const CITY = '渋谷';
public function hello()
{
echo 'hello! from ' . self::CITY;
}
}
class AnotherSample extends Sample
{
public const CITY = '新宿';
public function hello()
{
echo 'hello! from ' . self::CITY;
}
}
$anotherSample = new AnotherSample();
$anotherSample->hello();
コードの実行結果は下記の通りです。AnotherSampleクラスの定数CITY
は、Sampleクラスのfinal定数であるCITY
をオーバーライドできないことを確認できます。
PHP Fatal error: AnotherSample::CITY cannot override final constant Sample::CITY in /Path/to/sample.php on line 14
なにがいいのか
- コードの理解が容易になる
先の説明の通り、継承やオーバーライドを禁止することができるため、うまく使えばコードの複雑さを生みがちなクラスの多重継承を防ぐことができます。そうすると、継承元の深くまでコードを追う必要がなくコードの見通しがよくなり、コードを理解しやすくなります。 - バグの発生を防げる
コードの見通しがよくなることに関連しますが、見通しがよくなるとコードの影響範囲等も捉えやすくなるため、バグの発生を防げるというメリットも得ることができます。 - 責務を明確にすることができコードをシンプルに保つことができる
クラスの継承ができると、ベースとなるクラスを継承した新クラスに新たにpublicなメソッドを追加して、様々なことができる一見便利そうなクラスが出来上がったりしますが、役割を持ちすぎて責務が曖昧になると後の負債に繋がることは多々あります。final
宣言で継承の禁止を強制することで、そういったリスクの発生を防ぎ責務が明確なクラスやメソッドを維持することができます。 final
宣言されている限りは、そのクラスやメソッド、定数は継承やオーバーライドされることを意識しなくて済むため修正等を行いやすい
まとめ
最後までお読み頂きありがとうございます。
PHPのfinal
キーワードについて改めて見直してみた個人の所感ですが、新規でクラスを作成する際はクラス単位でfinal
宣言、既存クラスにメソッドや定数を追加するようなケースは新たに追加するメソッド・定数にfinal
宣言を積極的に使っていいように感じました。
※会社やチーム方針がある場合はまずはメンバーと話し合ってみてくださいfinal
キーワードについて調べると、手当たり次第にfinal
宣言するものではないといった意見もあるようなので、実装を行う中で新たに見えてくる点があったら改めて記事にできればと思います。