假设我有一个模型界面:
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)
答案 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)
{
}
}