覆盖接口实现中的参数类型

时间:2015-07-20 13:13:18

标签: php oop

假设我有一个模型界面:

interface Model
{
}

接口中的一个函数,它接收该模型作为参数:

interface Service
{
    public function add(Model $model);
}

为什么,当我用另一个实现上述功能的Model实现该服务时:

class AnotherModel implements Model
{
}

class AnotherService implements Service
{
    public function add(AnotherModel $model);
}

我收到此错误:

  

致命错误:AnotherService :: add()的声明必须兼容   使用Service :: add(Model $ model)

4 个答案:

答案 0 :(得分:7)

扩展类或实现接口时,不允许对继承方法的任何类型或可见性约束比父类更严格。这意味着如果您在父级中有公共方法,则不允许将其设为受保护/私有。如果父方法接受某些特定类型的参数(在这种情况下为模型),则不允许将它接受的参数限制为某种更具体的类型。

这就是你在这里做的 - 你的服务::添加()接受模型类型的元素,但 AnotherService 中的实现接受 AnotherModel 类型的对象。这意味着即使您有另一个实现模型的类,例如“类YetAnotherModel实现Model ”, AnotherService :: add()将不接受它,因为YetAnotherModel不是AnotherModel的实例,即使它们实现相同的接口。 因此, AnotherService :: add()将不接受 YetAnotherModel 类的对象,即使您实施了服务接口“我接受所有模型我的添加()方法“。

有关详细信息,请参阅https://en.wikipedia.org/wiki/Liskov_substitution_principle

答案 1 :(得分:2)

首先,documentantion说:

  

实现接口的类必须使用与接口中定义的完全相同的方法签名。不这样做会导致致命的错误。

现在,这背后有一个很好的理由。接口向程序员提供有关实现该接口的所有类中的可用方法的信息。您的Service接口承诺,它具有add方法,可以处理类型为Model的参数。你不能通过仅为特定的模型子集提供实现来打破这种承诺。

将自己视为使用某些外部库的程序员。通常情况下,您不得不单独使用库接口,并且不希望查看实现(这正是PHP中引入接口的原因)。如果允许参数类型更改,那么接口对您来说将毫无用处,因为您必须寻找每个实现方案。

答案 2 :(得分:1)

您正在尝试修改最初在add中声明的方法Service::add的原型。实现接口时,方法的原型必须与接口中定义的相同(在此示例中为Service)。您必须使用public function add(AnotherModel $model);更改public function add(Model $model);。通过这种方式,您可以正确实现方法的界面。当您必须调用此方法时,您可以传递实现Model接口的对象,例如:

class AnotherService implements Service
{
    public function add(Model $model)
    {
        ...
    }
}

$anotherModel = new AnotherModel();
$anotherService = new AnotherService();

$anotherService->add($anotherModel);

答案 3 :(得分:-1)

<?php
interface IModel
{
}

class Model implements IModel
{
    // All your models should inherit from this class
}

class AnotherModel extends Model
{
}

interface IService
{
    public function add(Model $model);
}

class AnotherService implements IService
{
    public function add(Model $model)
    {
    }
}