协变和逆变真的感觉不自然

问题描述 投票:0回答:1

尝试在子项中使用属性总是会失败并出现以下错误:

致命错误:D::$prop 的类型必须是 A(如 C 类)

我读过有关https://www.php.net/manual/en/language.oop5.variance.php但这仍然很令人困惑,我真的找不到一个合乎逻辑的理由来阻止这样做东西。

无法使用实现接口的类并尊重该接口的约定而无法键入提示,这感觉非常不自然和令人沮丧。

比如下面的例子:

<?php

interface A {
    public function foo();
}

class B implements A {
    public function foo() {
        echo "foo";
    }
    
    public function bar() {
        echo "bar";
    }
}

class C {
    public function __construct(protected A $prop) {
        $this->prop->foo();
    }
}

class D extends C {
    public function __construct(protected B $prop) { // This can't be done even though B implements A interface !!!
        $this->prop->bar();
        parent::__construct($prop);
    }
}

$b = new B();
$d = new D($b);

我需要理解为什么它不可能,并有一个有效的例子来说明为什么它不能以这种方式运行,尽管这段代码看起来完全符合逻辑。

php covariance
1个回答
0
投票

假设您的代码可行,请考虑以下场景:

class C {
    public function setProp(A $prop) {
        $this->prop = $prop;
    }
}

class D extends C {
    public function getProp() : B {
        return $this->prop;
    }
}

class E implements A {}

$e = new E();
$d->setProp($e);
$e = $d->getProp(); # Should return an instance of B
                    # but it will return an instance of E.

显然,这是出乎意料的。因此 PHP 不允许您重新声明不同类型的属性。

你可以做的就是在构造函数中定义参数类型。如果你需要B的成员提示,你可以声明另一个方法。

class D extends C {
    public function __construct(B $prop) { # No protected
        parent::__construct($prop);
        $this->prop->bar(); # This is legal, but your IDE may complain
        $this->getProp()->bar();
    }
    
    protected function getProp() : B {
        return $this->prop;
    }
}
最新问题
© www.soinside.com 2019 - 2024. All rights reserved.