您可以使用不同但“兼容”的签名覆盖接口方法吗?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了您可以使用不同但“兼容”的签名覆盖接口方法吗?相关的知识,希望对你有一定的参考价值。

考虑以下php接口:

interface Item {
    // some methods here
}

interface SuperItem extends Item {
    // some extra methods here, not defined in Item
}

interface Collection {
    public function add(Item $item);
    // more methods here
}

interface SuperCollection extends Collection {
    public function add(SuperItem $item);
    // more methods here that "override" the Collection methods like "add()" does
}

我正在使用PHPStorm,当我这样做时,我在IDE中得到一个错误,基本上陈述add()SuperCollection的定义与它扩展的接口Collection中的定义不兼容。

在某种程度上,我可以看到这是一个问题,因为该方法的签名与它“完全覆盖”的签名不匹配。但是,我觉得这是兼容的,因为SuperItem延伸Item,所以我会像add(SuperItem)一样查看add(Item)

我很好奇这是否支持PHP(版本5.4或更高版本),并且可能IDE有一个错误,无法正确捕获它。

答案

不,我非常确定PHP在任何版本中都不支持这一点,而且它宁可打败界面。

接口的关键在于它为您提供了与引用相同接口的其他代码的固定合同。

例如,考虑这样的函数:

function doSomething(Collection $loopMe) { ..... }

此函数期望接收实现Collection接口的对象。

在函数内,程序员可以编写对Collection中定义的方法的调用,知道该对象将实现这些方法。

如果你有一个像这样的重写接口,那么你有一个问题,因为SuperCollection对象可以传递给函数。它会工作,因为它继承了它也是一个Collection对象。但是函数中的代码再也不能确定它知道add()方法的定义是什么。

根据定义,接口是固定合同。这是不可改变的。

作为替代方案,您可以考虑使用抽象类而不是接口。这将允许您在非严格模式下覆盖,但如果您使用严格模式仍然会出现错误,原因相同。

另一答案

作为一种解决方法,我在接口中使用PHPDoc块。

interface Collection {
   /**
    * @param Item $item
    */
    public function add($item);
    // more methods here
}

interface SuperCollection extends Collection {
    /**
    * @param SuperItem $item
    */
    public function add($item);
    // more methods here that "override" the Collection methods like "add()" does
}

这样,如果您正确使用接口,IDE应该可以帮助您捕获一些错误。您也可以使用similar technique覆盖返回值类型。

另一答案

您无法更改方法参数。

http://php.net/manual/en/language.oop5.interfaces.php

另一答案

问题不在IDE中。在PHP中,您无法覆盖方法。并且兼容性只是相反的方向 - 您可以安全地期望父类的实例并接收子类。但是当您期望一个子类时,如果您收到父类,则无法安全 - 子类可能会定义父类中不存在的方法。但是,你仍然无法覆盖该方法

另一答案

当我有一个我可能需要重载的方法(PHP不支持)时,我确保其中一个方法参数(通常是最后一个)是一个数组。通过这种方式,我可以通过我需要的任何东西。然后我可以在函数内测试各种数组元素,告诉我需要执行的方法中的例程,通常是在select / case中。

另一答案

不允许扩展接口更改方法定义。如果您的SuperItem正在扩展Item,它应该通过实现Collection接口的类传递而不会出现问题。

但根据你真正想做的事情,你可以尝试:

  • 使用稍微不同的SuperItem方法创建接口并实现: interface SuperCollection extends Collection { public function addSuper(SuperItem $superItem); }
  • 使用装饰器模式创建几乎相同的接口而不扩展: interface Collection { public function add(Item $item); // more methods here } interface SuperCollection { public function add(SuperItem $item); // more methods here that "override" the Collection methods like "add()" does } 然后装饰器(抽象)类,将使用此接口: class BasicCollection implements Collection { public function add(Item $item) { } } class DecoratingBasicCollection implements SuperCollection { protected $collection; public function __construct(Collection $collection) { $this->collection = $collection; } public function add(SuperItem $item) { $this->collection->add($item); } }

以上是关于您可以使用不同但“兼容”的签名覆盖接口方法吗?的主要内容,如果未能解决你的问题,请参考以下文章

您可以使用泛型进行方法重载并且只更改方法签名的泛型类型吗?

您可以在 Visual Studio 构建过程中“覆盖”来自不同文件夹的代码吗?

如何创建具有可变参数/不同方法签名的方法接口?

C#中具有相同名称和签名但返回类型不同的方法

使用不同的兼容类型覆盖属性

在 TypeScript 接口中覆盖/覆盖方法签名