2013-10-17 46 views
12

我无法弄清楚为什么这段代码在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); 
} 

?> 

它似乎不会破坏任何OOP规则,但它为什么会给我一个错误?

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

回答

18

确实休息SOLID规则。您声明Car::setEngine接受Engine类型的一个参数,但子WaterCar::setEngine接受HydroEngine类型的参数。即使HydroEngineEngine的子类型,它仍然是一个不同的类型。

当一个类Foo implements WaterCar,这也是事实,这个类是instanceof Car。但Foo::setEngine接受HydroEngine,但不接受Engine。所以Foo::setEngine据推测implements Car,但不接受Engine类型的参数。这打破了Liskov substitution principle。您无法更改子类别界面中的参数类型。

继承的关键字是明确的extends。子类别与父类别完全相同,并且可能更多。它不能做比母公司少。由于HydroEngineEngine的专用子类型,因此它意味着WaterCar确实更少Car,因为它只接受更窄的子类型Engine。例如: -

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

如果您在WaterCar过去了,上面的代码会破灭,因为它不接受Engine

+0

可能是我之前误解了LSP ..但我认为Java允许你做这件事(我错了?) – pleerock

+0

不知道。 PHP不。 :)我希望解释为什么对你有意义。 – deceze

+0

是的,谢谢。我在考虑''HydroEngine'确实与* Engine完全相同或更多*然后'Engine' - 这就是为什么它可以接受lsp,但现在您的解释确实有意义 – pleerock

1

我觉得方法签名仍然需要是完全一样的,因为在编译时它不工作了,如果HydroEngine是一个引擎。

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