pleerock
pleerock

Reputation: 18836

Override method parameter with child interface as a new parameter

I cant figure out why this code does not work in 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);
}

?>

It seems it does not break any OOP rules, but why it gives me an error?

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

Upvotes: 16

Views: 6100

Answers (2)

deceze
deceze

Reputation: 522005

It does break SOLID rules. You declare Car::setEngine to accept one parameter of type Engine, but the child WaterCar::setEngine accepts a parameter of type HydroEngine. Even if HydroEngine is a subtype of Engine, it's still a different type.

When a class Foo implements WaterCar, it is also true that this class is an instanceof Car. But Foo::setEngine accepts a HydroEngine, but does not accept an Engine. So Foo::setEngine supposedly implements Car, yet does not accept a parameter of type Engine. Which breaks the Liskov substitution principle. You cannot change the type of parameters in subclassed interfaces, period.

The keyword for inheritance is explicitly extends. A subclass does exactly the same as the parent class and possibly more. It cannot do less than the parent. Since HydroEngine is a specialized subtype of Engine, this would mean a WaterCar does less than a Car, since it only accepts a more narrow subtype of Engine. E.g.:

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

The above code would implode if you passed in a WaterCar, because it does not accept an Engine.

Upvotes: 31

BadHorsie
BadHorsie

Reputation: 14544

I think the method signature still needs to be exactly the same because at compile time it doesn't work out if HydroEngine is an Engine.

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

Upvotes: 1

Related Questions