Подсказка типа Php не согласуется с интерфейсами и абстрактными классами?

Я думаю, что гораздо проще увидеть проблему в примере кода, чем писать вопрос в первую очередь. Вот мой php-код:

<?php

interface AnInterface
{
        public function method();
}    

class AClass implements AnInterface
{
        public function method()
        {
                echo __METHOD__;
        }
}    

abstract class AnAbstractClass
{
        abstract public function method( AnInterface $Object );
}

class ConcreteClass extends AnAbstractClass
{
        public function method( AClass $Object )
        {
                $Object->method();
        }
}

$Object1 = new ConcreteClass();
$Object2 = new AClass();

$Object1->method( $Object2 );

Приведенный выше код вызывает следующую ошибку:

Неустранимая ошибка: объявление ConcreteClass:: method() должно быть совместимо с объявлением AnAbstractClass:: method()

Проблема заключается в том, что php, похоже, не распознает подписи метода AnAbstractClass:: method и ConcreteClass:: как совместимый. Я делаю что-то неправильно? Спасибо!

Ответ 1

php, похоже, не распознает подписи AnAbstractClass::method и ConcreteClass::method как совместимые.

PHP правильный, они не совместимы. Предоставляя только экземпляры AClass (или его дочерних элементов) для передачи в ConcreteClass::method, вы нарушаете контракт, который AnAbstractClass предоставляет: Любой из его подклассов должен принимать AnInterface в качестве аргумента для своего method().

Если ваш пример работал, и у меня был другой класс BClass, реализующий AnInterface, у нас была бы ситуация, когда согласно AnAbstractClass, method() должны принимать экземпляры BClass, тогда как согласно ConcreteClass, это не должно быть.

Измените свою подпись для ConcreteClass::method, чтобы она соответствовала значению AnAbstractClass::method.

Ответ 2

Не вычисляется. Вчера у нас было такое же обсуждение:
Могут ли типы параметров быть специализированными в PHP

Все ваши производные классы должны точно реализовать сигнатуры метода.

Это то, что в идеале должно быть проверено во время выполнения. Но в PHP парсер делает. (Чтобы компенсировать, PHP не проверяет доступ к закрытому/защищенному атрибуту во время разбора, но пусть это скорее взорвется во время выполнения.)

Если вы хотите применить более строгий тип, я бы посоветовал:

 assert( is_a($Object, "AClass") );

Ответ 3

Вот пример, который показывает, почему это не разрешено:

<?php
class BClass implements AnInterface { }

function moo(AnAbstractClass $abstract)
{
    $b = new BClass();
    $abstract->method($b);
}

Это будет действительный код, но он не сработает, если вы передадите ConcreteClass в moo, потому что его метод ConcreteClass::method не позволяет BClass.

Это сложно, но это легче понять, если вы видите пример.