PHP Velho Oeste 2024

列挙型が継承できない理由

クラスは、メソッドの使い方を約束(契約)するものです:

<?php

class A {}
class
B extends A {}

function
foo(A $a) {}

function
bar(B $b) {
foo($b);
}
?>

上のコードは型安全といえます。なぜなら、 B は A との契約を守っており、かつ共変性/反変性のマジックによって、 メソッドへのあらゆる期待が満たされるからです。例外は別です。

一方で、列挙型は case について契約するものです。メソッドではありません:

<?php

enum ErrorCode {
case
SOMETHING_BROKE;
}

function
quux(ErrorCode $errorCode)
{
// 以下のようなコードを書くと、全ての case をカバーします
match ($errorCode) {
ErrorCode::SOMETHING_BROKE => true,
}
}

?>

quux 関数内の match 式は、 ErrorCode の全ての case をカバーしているかを静的に解析できます。

ここで以下のように、列挙型が継承可能だとしましょう:

<?php

// 列挙型が final でなかったと仮定した、思考実験のコード
// このコードは実際には動作しないので注意
enum MoreErrorCode extends ErrorCode {
case
PEBKAC;
}

function
fot(MoreErrorCode $errorCode) {
quux($errorCode);
}

fot(MoreErrorCode::PEBKAC);

?>

通常の継承のルールでは、あるクラスの子クラスは親クラスの型チェックを通過します。

上のコードの問題点は、 quux() 関数の match 式が全ての case をカバーしてはいないということです。 なぜなら、MoreErrorCode::PEBKAC がカバーされていないので、match 式が例外をスローするからです。

こうした理由から、列挙型は final 扱いであり、継承できなくなっています。

add a note

User Contributed Notes

There are no user contributed notes for this page.
To Top