22

考虑以下 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()inSuperCollection的定义与它扩展的接口中的定义不兼容,Collection.

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

我很好奇 PHP(版本 5.4 或更高版本)是否支持此功能,也许 IDE 存在无法正确捕获此问题的错误。

4

7 回答 7

15

不,我很确定 PHP 在任何版本中都不支持这一点,而且它宁愿破坏接口的意义。

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

例如,考虑这样的函数:

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

这个函数期望接收一个实现Collection接口的对象。

在函数中,程序员将能够编写对定义在 中的方法的调用Collection,因为他们知道对象将实现这些方法。

如果你有一个像这样被覆盖的接口,那么你就会遇到问题,因为SuperCollection可以将一个对象传递给函数。Collection它会起作用,因为由于继承它也是一个对象。但随后函数中的代码不再确定它知道add()方法的定义是什么。

根据定义,接口是一个固定的合同。它是不可变的。

作为替代方案,您可以考虑使用抽象类而不是接口。这将允许您在非严格模式下进行覆盖,尽管出于同样的原因,如果您使用严格模式,您仍然会收到错误。

于 2013-02-21T20:44:26.413 回答
3

2014 年 11 月发布的 PHP 7.4改进了类型变化

问题中的代码仍然无效:

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); // This will still be a compile error
    // more methods here that "override" the Collection methods like "add()" does
}

因为Collection接口保证任何实现它的东西都可以接受任何类型Item的对象作为add.

但是,以下代码在 PHP 7.4 中有效:

interface Item {
    // some methods here
}

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

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

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

在这种情况下Collection保证它可以接受任何SuperItem. 由于所有SuperItem的 s 都是Items,SuperCollection所以也做了这个保证,同时也保证它可以接受任何其他类型的Item. 这称为逆变方法参数类型

在早期版本的 PHP 中存在有限的类型变化形式。假设其他接口与问题中的一样,则SuperCollection可以定义为:

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

这可以解释为意味着任何值都可以传递给该add方法。这当然包括所有Item的 s,所以这仍然是类型安全的,或者它可以解释为意味着一个未指定的值类,通常记录为mixed可以传递,程序员需要使用其他知识来确切地知道什么可以使用该函数.

于 2019-07-06T17:05:47.803 回答
3

作为一种解决方法,我在接口中使用 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 应该可以帮助您捕获一些错误。您也可以使用类似的技术来覆盖返回值类型。

于 2016-04-09T14:33:36.010 回答
2

You cannot change the methods arguments.

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

于 2013-02-21T20:38:40.657 回答
1

扩展接口不允许更改方法定义。如果您的 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);
        }
    }
    
于 2018-03-06T10:55:26.570 回答
0

The problem is not in the IDE. In PHP you cannot override a method. And the compatibility is only in the opposite direction - you can safely expect an instance of the parent class and receive a subclass. But when you expect a subclass, you cannot be safe if you receive the parent class - the subclass may define methods that do not exist in the parent. But still, you can't override the method

于 2013-02-21T20:38:51.673 回答
0

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

于 2015-12-11T22:11:58.050 回答