Объявление Specific::method(): должно быть совместимо с General::method(). PHP не прав насчет LSP?
Я полагаю, что "... должно быть совместимо с..." для обеспечения соблюдения принципа замены Лискова. Но я не уверен, что это то, что говорит LSP?
У меня есть такой код:
class General
{
public static function create(): General
{
return new static;
}
public function doSomething()
{
echo get_class($this) . ' speaking!' . PHP_EOL;
}
}
class Specific extends General
{
public static function create(): Specific
{
return parent::create();
}
}
function doSomething(General $object)
{
$object->doSomething();
}
doSomething(General::create());
doSomething(Specific::create());
Который производит:
Неустранимая ошибка PHP: объявление Specific::create(): Specific должно быть совместимо с General::create(): General в...
LSP часто упоминается как:
Функции, которые используют указатели или ссылки на базовые классы, должны иметь возможность использовать объекты производных классов, не зная об этом.
И это не нарушено здесь, насколько я понимаю. Так что здесь не так? Это какое-то особое ограничение, которое не имеет ничего общего с LSP? Это ошибка в PHP? Я делаю что-то не так, не зная?
ОБНОВЛЕНИЕ: я нашел этот поток ( ковариация типа параметра в специализациях). Я понимаю и полностью согласен, что пример там нарушает LSP. Но у меня ситуация другая (на самом деле наоборот).
1 ответ
http://php.net/manual/en/functions.returning-values.php
При переопределении родительского метода дочерний метод должен соответствовать любому объявлению возвращаемого типа в родительском методе. Если родитель не определяет тип возвращаемого значения, тогда дочерний метод может сделать это.
Ваш Specific::create
функция должна указывать General
тип возврата.
В противном случае код написан для Specific::create
потенциально может сломаться при запуске General::create
как бы получил другой класс.
LSP утверждает, что параметры метода должны быть контравариантными, а возвращаемые значения должны быть ковариантными.
В вашем случае у вас есть ковариантные возвращаемые типы, которые удовлетворяют LSP.
Проблема в самом PHP. Это ограничение не имеет ничего общего с LSP, просто в более ранних версиях PHP это еще не реализовано.
Начиная с PHP 7.2 (7.4) он теперь полностью поддерживает LSP, то есть контравариантность параметров и ковариацию возвращаемых значений: https://www.php.net/manual/en/language.oop5.variance.php
UPD. Но ваш код содержит другую проблему: ваш
\Specific::create()
метод должен возвращать экземпляр
Specific
как вы указали в его подписи, но он пытается вернуть значение, возвращаемое
\General::create()
который заявлен как
General
(и мы знаем, что экземпляры
General
не
instanceof Specific
). Это вводит в заблуждение. Например, PhpStorm предупредит вас об этом:
Return value is expected to be 'Specific', 'General' returned
. Но PHP не выдает ошибок по этому поводу.
Это можно исправить, добавив PhpDoc в
\General::create()
:
class General
{
/**
* @return static
*/
public static function create(): General
{
return new static;
}