2013-10-17 87 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); 
}