学习路径
/
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 编辑 该链接会在新窗口或标签页中打开