Параметр переопределить метод с дочерним интерфейсом в качестве нового параметра

Я не могу понять, почему этот код не работает в PHP?

<?php

interface Engine {

    function run();
}

interface HydroEngine extends Engine {

    function run();
}

interface Car {

    function setEngine(Engine $engine);

}

interface WaterCar extends Car {

    function setEngine(HydroEngine $engine);
}

?>

Кажется, он не нарушает никаких правил ООП, но почему это дает мне ошибку?

Fatal error: Declaration of WaterCar::setEngine() must be compatible with Car::setEngine(Engine $engine)

Ответ 1

Он нарушает правила SOLID. Вы объявляете Car::setEngine принимать один параметр типа Engine, но дочерний элемент WaterCar::setEngine принимает параметр типа HydroEngine. Даже если HydroEngine является подтипом Engine, он все еще отличается от типа.

Когда класс Foo implements WaterCar, также верно, что этот класс является instanceof Car. Но Foo::setEngine принимает HydroEngine, но не принимает Engine. Итак, Foo::setEngine предположительно implements Car, но не принимает параметр типа Engine. Что нарушает принцип замещения Лискова. Вы не можете изменить тип параметров в подклассах интерфейсов, период.

Ключевое слово для наследования явно extends. Подкласс делает то же самое, что и родительский класс и, возможно, больше. Он не может делать меньше, чем родитель. Так как HydroEngine является специализированным подтипом Engine, это означает, что WaterCar делает меньше, чем a Car, поскольку он принимает только более узкий подтип Engine. Например:.

function (Car $car) {
    $engine = new EngineImplementation;
    $car->setEngine($engine);
}

Вышеприведенный код будет развязываться, если вы передали в WaterCar, потому что он не принимает Engine.

Ответ 2

Я думаю, что подпись метода все равно должна быть точно такой же, потому что во время компиляции это не сработает, если HydroEngine - это Engine.

interface WaterCar extends Car {
    function setEngine(Engine $engine);
}