PHP 7中的抽象函数参数类型提示覆盖 [英] Abstract function parameter type hint overriding in PHP 7
问题描述
是否可以用子类中的函数覆盖PHP 7中的抽象函数,从而缩小可接受的参数类型?
Is it possible to override an abstract function in PHP 7 with a function in a child class that would narrow down on the accepted argument type?
一言以蔽之-假设我有一个抽象方法:
A word of elaboration - let's assume that I have an abstract method:
abstract public function setTarget(
AbstractDeliveryTarget $deliveryTarget
): Delivery;
abstract public function setTarget(
AbstractDeliveryTarget $deliveryTarget
): Delivery;
现在在一个子类中,我想重写此方法的签名,以便它仅接受特定的参数类型(假设EmailDeliveryTarget
扩展了AbstractDeliveryTarget
):
Now in one child class, I'd like to override this method's signature in order for it to accept only a specific parameter type (let's assume that EmailDeliveryTarget
extends AbstractDeliveryTarget
):
public function setTarget(
EmailDeliveryTarget $emailDeliveryTarget
): Delivery;
public function setTarget(
EmailDeliveryTarget $emailDeliveryTarget
): Delivery;
然后,PHP解释器会抱怨不能在子类中更改abstract方法的签名.那么,是否有一种方法可以在子类中实现某种方法安全性,而不是某些方法内类型类型防护?
PHP interpreter would then complain that the signature of the abstract method cannot be altered in the child class. Is there, then, a way to achieve that type-safety in a child class other than some in-method-body type guards?
推荐答案
这不是技术限制;您要的内容与OOP的原则无关.
This is not a technical limitation; what you're asking for doesn't make sense with the principles of OOP.
您的抽象类是合同;让我们定义基类:
Your abstract class is a contract; let's define the base class:
class AbstractDeliverer {
abstract public function setTarget(
AbstractDeliveryTarget $deliveryTarget
): Delivery;
}
还有一些投放目标
class AbstractDeliveryTarget {}
class EmailDeliveryTarget extends AbstractDeliveryTarget {}
class SMSDeliveryTarget extends AbstractDeliveryTarget {}
然后您可以将其写在其他地方:
Then you can write this somewhere else:
function deliverAll(AbstractDeliverer $d) {
$e = new EmailDeliveryTarget;
$d->setTarget($e);
$s = new SMSDeliveryTarget;
$d->setTarget($s);
}
因为我们知道$d
是AbstractDeliverer
,所以我们知道将$e
和$s
传递给它都应该起作用.这是我们确保输入的内容属于我们时所保证的合同.
Because we know that $d
is an AbstractDeliverer
, we know that passing $e
and $s
to it should both work. That is the contract guaranteed to us when we made sure our input was of that type.
现在,让我们看看如果按照您想要的方式扩展它会发生什么:
Now lets see what happens if you extend it the way you wanted:
class EmailOnlyDeliverer extends AbstractDeliverer {
public function setTarget(
EmailDeliveryTarget $emailDeliveryTarget
): Delivery {
/* ... */
}
}
$emailonly = new EmailOnlyDeliverer;
我们知道$e instanceOf AbstractDeliverer
将是true
,因为我们已经继承了,所以我们知道可以安全地调用deliverAll
方法:
We know that $e instanceOf AbstractDeliverer
will be true
, because we've inherited, so we know we can safely call our deliverAll
method:
deliverAll($emailonly);
该函数的第一部分很好,并且可以有效地运行它:
The first part of the function is fine, and will effectively run this:
$e = new EmailDeliveryTarget;
$emailonly->setTarget($e);
但是接下来我们要碰到这一部分:
But then we hit this part:
$s = new SMSDeliveryTarget;
$emailonly->setTarget($s);
糟糕!致命错误!但是AbstractDeliverer
上的合同告诉我们,这是可以通过的正确值!出了什么问题?
Oops! Fatal error! But the contract on AbstractDeliverer
told us this was the correct value to pass! What went wrong?
规则是,子类必须接受父类将接受的所有输入,但是如果需要,它可以接受其他输入,这被称为"contravariance". (相反,返回类型是协变量":子类绝不能返回无法从父类返回的值,但可以做出更强的保证,即它只会返回这些值的子集;我将离开您自己拿出一个例子.
The rule is that a sub-class must accept all inputs that the parent class would accept, but it can accept additional inputs if it wants, something known as "contravariance". (Return types are instead "covariant": the sub-class must never return a value which couldn't be returned from the parent class, but can make a stronger promise that it will only return a subset of those values; I'll leave you to come up with an example of that one yourself).
这篇关于PHP 7中的抽象函数参数类型提示覆盖的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!