2010-12-30 2 views
13

Я думаю, что гораздо проще увидеть проблему в примере кода, чем писать вопрос в первую очередь. Вот мой 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); 

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

Fatal error: Declaration of ConcreteClass::method() must be compatible with that of AnAbstractClass::method()

Проблема заключается в том, что, кажется, не PHP, признают подписи AnAbstractClass :: метода и ConcreteClass: : метод совместим. Я делаю что-то неправильно? Благодаря!

+4

Пожалуйста, * пожалуйста * ознакомьтесь с сообщением об ошибке, которое генерирует ваш код. Проводка кода без публикации вывода (или ошибки) бесполезна. – meagar

ответ

24

php doesn't seem to be recognizing the signatures of AnAbstractClass::method and ConcreteClass::method as compatible.

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

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

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

+3

Немного подумав, я пришел к выводу, что вы правы. Кажется, я пытался пропустить образец. Большое спасибо. – Muc

+0

Могу ли я переопределить его в phpdoc для автозаполнения в моей среде IDE (phpStorm)? Потому что теперь он не дает мне эту способность – Crusader

2

Не вычисляет. Вчера у нас было такое же обсуждение:
Can parameter types be specialized in PHP

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

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

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

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

Учитывая, что это PHP 5, это утверждение должно читать 'assert ($ Object instanceof AClass);' – BoltClock

+0

Да, это может сработать, но на самом деле это не так, как должно быть, я думаю, что у меня было заблуждение в первую очередь , см. ответ от @meagar – Muc

+0

Случаи, подобные наследованию от ArrayObject, будут ISTM хорошими кандидатами для выполнения утверждения типа или метания, если был получен неправильный тип. Но вы не можете изменить базовый интерфейс (например, ArrayAccess :: offsetSet всегда будет принимать смешанный параметр для значения). –

2

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

<?php 
class BClass implements AnInterface { } 

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

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

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

Смежные вопросы