列挙型とメソッド

列挙型では、(Pure Enum と Backed Enum ともに) メソッドを含めることもできますし、 インターフェイスを実装することもできます。 列挙型がインターフェイスを実装する場合、 そのインターフェイスでの型チェックは、 実装した列挙型の全ての case で有効です。

<?php

interface Colorful
{
    public function color(): string;
}

enum Suit implements Colorful
{
    case Hearts;
    case Diamonds;
    case Clubs;
    case Spades;

    // インターフェイスの規約を満たすための実装
    public function color(): string
    {
        return match($this) {
            Suit::Hearts, Suit::Diamonds => 'Red',
            Suit::Clubs, Suit::Spades => 'Black',
        };
    }

    // インターフェイスの一部ではないが、正しいコード
    public function shape(): string
    {
        return "Rectangle";
    }
}

function paint(Colorful $c)
{
   /* ... */
}

paint(Suit::Clubs);  // 動作します

print Suit::Diamonds->shape(); // "Rectangle" と表示
?>

上記の例の Suit の4つのインスタンスは、 全てふたつのメソッド color()shape() を持ちます。 コードの呼び出しと型チェックに限れば、 これらのメソッドは他のオブジェクトのインスタンスと全く同じ振る舞いをします。

Backed Enum の場合、 インターフェイスの宣言は依存する型の宣言の直後に置きます。

<?php

interface Colorful
{
    public function color(): string;
}

enum Suit: string implements Colorful
{
    case Hearts = 'H';
    case Diamonds = 'D';
    case Clubs = 'C';
    case Spades = 'S';

    // インターフェイスの規約を満たすための実装
    public function color(): string
    {
        return match($this) {
            Suit::Hearts, Suit::Diamonds => 'Red',
            Suit::Clubs, Suit::Spades => 'Black',
        };
    }
}
?>

メソッドの内部では、 変数 $this が定義され、 case のインスタンスを参照します。

メソッドには任意の複雑な処理を記述できますが、 実際、通常は静的な値を返したり、 case 毎に異なる結果を提供するために match$this と共に使う処理を行います。

この場合は、Red と Black の値を持ち、 それらを返す SuitColor という列挙型を定義する方が、 より良いデータモデリングのプラクティスであることにも注意して下さい。 しかし、そうすると上の例が複雑になるでしょう。

上で示した階層は、論理的には次のようなクラス階層に似ています (しかし、以下のコードは実際には動作しません):

<?php

interface Colorful
{
    public function color(): string;
}

final class Suit implements UnitEnum, Colorful
{
    public const Hearts = new self('Hearts');
    public const Diamonds = new self('Diamonds');
    public const Clubs = new self('Clubs');
    public const Spades = new self('Spades');

    private function __construct(public readonly string $name) {}

    public function color(): string
    {
        return match($this) {
            Suit::Hearts, Suit::Diamonds => 'Red',
            Suit::Clubs, Suit::Spades => 'Black',
        };
    }

    public function shape(): string
    {
        return "Rectangle";
    }

    public static function cases(): array
    {
        // 不正なメソッド。列挙型では、手動で cases() メソッドを定義できないからです。
        // "値のリスト" のページも参照ください。
    }
}
?>

メソッドには public, private, protected が指定できますが、 列挙型では実際 private と protected は同じものです。 なぜなら、継承が許されていないからです。