是否可以使用子类中的函数覆盖PHP 7中的抽象函数,该函数将缩小接受的参数类型?
详细阐述 - 让我们假设我有一个抽象的方法:
abstract public function setTarget(
AbstractDeliveryTarget $deliveryTarget
): Delivery;
现在在一个子类中,我想覆盖此方法的签名,以便它只接受特定的参数类型(假设EmailDeliveryTarget
扩展AbstractDeliveryTarget
):
public function setTarget(
EmailDeliveryTarget $emailDeliveryTarget
): Delivery;
答案 0 :(得分:5)
这不是技术限制;你要求的东西对OOP的原则没有意义。
你的抽象类是合同;让我们定义基类:
class AbstractDeliverer {
abstract public function setTarget(
AbstractDeliveryTarget $deliveryTarget
): Delivery;
}
和一些交付目标
class AbstractDeliveryTarget {}
class EmailDeliveryTarget extends AbstractDeliveryTarget {}
class SMSDeliveryTarget extends AbstractDeliveryTarget {}
然后你可以把它写在其他地方:
function deliverAll(AbstractDeliverer $d) {
$e = new EmailDeliveryTarget;
$d->setTarget($e);
$s = new SMSDeliveryTarget;
$d->setTarget($s);
}
因为我们知道$d
是AbstractDeliverer
,所以我们知道将$e
和$s
传递给它应该都有效。当我们确保输入属于那种类型时,这是我们保证的合同。
现在让我们看看如果按照您想要的方式扩展会发生什么:
class EmailOnlyDeliverer extends AbstractDeliverer {
public function setTarget(
EmailDeliveryTarget $emailDeliveryTarget
): Delivery {
/* ... */
}
}
$emailonly = new EmailOnlyDeliverer;
我们知道$e instanceOf AbstractDeliverer
将true
,因为我们已经继承了,所以我们知道我们可以安全地调用我们的deliverAll
方法:
deliverAll($emailonly);
该函数的第一部分很好,并且将有效地运行:
$e = new EmailDeliveryTarget;
$emailonly->setTarget($e);
但接下来我们点击了这一部分:
$s = new SMSDeliveryTarget;
$emailonly->setTarget($s);
糟糕!致命错误!但AbstractDeliverer
上的合同告诉我们这是正确的价值!出了什么问题?
规则是一个子类必须接受父类可以接受的所有输入,但是如果它想要它可以接受其他输入,这被称为" contravariance" 。 (返回类型是"协变":子类必须永远不会返回一个不能从父类返回的值,但可以做出更强的承诺,它只会返回一个子类那些价值观;我会让你自己想出一个这样的例子。
答案 1 :(得分:0)
看起来没有漂亮的解决方案,所以我们可以尝试使用接口。
<?php
interface DeliveryTarget {}
class AbstractDeliveryTarget implements DeliveryTarget {
}
class EmailDeliveryTarget extends AbstractDeliveryTarget{
}
abstract class Q {
abstract protected function action(DeliveryTarget $class);
}
class Y extends Q {
protected function action(DeliveryTarget $class){}
}
OR
interface DeliveryTargetAction {}
class AbstractDeliveryTarget {
}
class EmailDeliveryTarget extends AbstractDeliveryTarget implements DeliveryTargetAction {
}
abstract class Q {
abstract protected function action(DeliveryTargetAction $class);
}
class Y extends Q {
protected function action(DeliveryTargetAction $class){}
}
取决于你想做什么。