軌道
/
PHP
PHP
/
課程大綱
/
協變與逆變
協變

協變與逆變 在 PHP

{other: "%{count} 個練習"}

關於 協變與逆變

共變與逆變

使用聯合型別時,你必須留意共變與逆變這兩個概念。文件中有說明,不過這些範例或許有幫助。

假設我們有一個 Calculator 類別,使用像上面範例那樣的函式:

class Calculator
{
    public function add(int|float $x, int|float $y): int|float
    {
        return $x + $y;
    }
}

如果你要擴充這個類別,可以變更參數型別和回傳型別,但必須遵守下列規則。

首先,參數型別可以更「寬」,也就是能接受比基底類別更多的型別。例如下面這樣是允許的:

class StringCalc extends Foo
{
    public function add(float|int|string $x, float|int|string $y): int|float
    {
        if (!is_numeric($x) || !is_numeric($y)) {
            throw new \InvalidArgumentException('$x and $y must be numeric');
        }
        return $x + $y;
    }
}

我們可以用StringCalc取代原本的Calculator,一切仍然能正常運作。呼叫端仍然可以傳入原本就能傳的float或int,回傳型別也仍然是預期的int或float。

同樣地,你也可以縮小回傳型別,也就是從聯合型別中移除某些型別,讓它更明確。下面這個範例就運用了這項規則:

class IntCalc extends Calculator
{
    public function add(float|int $x, float|int $y) : int
    {
        return (int)($x + $y);
    }
}

由於IntCalc仍然接受int和float,它可以毫無問題地取代原本的Calculator。

如果想進一步了解哪些寫法可以、哪些不行,可以參考里氏替換原則(LSP)。

想看一個不被允許的例子,請看看下面的InvalidCalc:

class InvalidCalc extends Calculator
{
    public function add(int $x, int $y) : int|float|string
    {
        // Declaration must be compatible with Calculator->add(x: float|int, y: float|int)

        // Return type declaration must be compatible with Calculator->add(x: float|int, y: float|int) : float|int
    }
}

InvalidCalc類別無法編譯,而且有兩個問題。由於縮減了參數允許的型別,InvalidCalc無法取代原本的Calculator,因為它不再接受float。此外,它現在可以回傳string,這也不在原本類別允許的回傳型別之內。

透過 GitHub 編輯 連結會在新視窗或分頁中開啟