PHP 7中的抽象函数参数类型提示覆盖 [英] Abstract function parameter type hint overriding in PHP 7

查看:124
本文介绍了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);
}

因为我们知道$dAbstractDeliverer,所以我们知道将$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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆