【问题标题】:Override method parameter with child interface as a new parameter以子接口覆盖方法参数作为新参数
【发布时间】:2013-10-26 01:59:22
【问题描述】:

我不明白为什么这段代码在 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)

【问题讨论】:

    标签: php oop


    【解决方案1】:

    确实违反了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。子类与父类完全相同,并且可能更多。它不能做 less 比父母。由于HydroEngineEngine 的特殊子类型,这意味着WaterCarCar 执行更少,因为它只接受更窄的Engine 子类型。例如:

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

    如果你传入WaterCar,上面的代码会崩溃,因为它不接受Engine

    【讨论】:

    • 可能我之前误解了 LSP.. 但我认为 Java 允许你做这件事(我错了吗?)
    • 不知道。 PHP 没有。 :) 我希望解释为什么对你有意义。
    • 好的,谢谢。我在想HydroEngine 确实完全相同或更多然后Engine - 这就是为什么它可以接受 lsp,但现在你的解释确实有意义
    • 但是考虑一个实例$wc,它实现了WaterCar,并被传递给一个期望实现Engine的函数/方法。它通过instanceof(Engine) 测试对象,然后将IonEngine 传递给watercar 实例...boom。 Eiffel 是我所知道的唯一一种允许协变重定义的语言。
    • @Volker 是的,这是一个很棒的功能。但是 PHP,正如你所说的大多数其他语言,不实现“接受 x 类型的东西”,而只实现“是 x 类型”。
    【解决方案2】:

    我认为方法签名仍然需要完全相同,因为在编译时,如果 HydroEngine 是一个引擎,它就不起作用。

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

    【讨论】:

      猜你喜欢
      • 2019-04-08
      • 1970-01-01
      • 1970-01-01
      • 2021-02-20
      • 1970-01-01
      • 2023-04-01
      • 1970-01-01
      • 2017-03-05
      相关资源
      最近更新 更多