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

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

    <?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 扱いであり、継承できなくなっています。

`↑` and `↓` to navigate • `Enter` to select • `Esc` to close • `/` to open