为什么PHP允许“不兼容”的构造函数?

15 浏览
0 Comments

为什么PHP允许“不兼容”的构造函数?

以下是一些代码片段:

  1. 覆盖构造方法有一个额外的参数。

    class Cat {
        function __construct() {}
    }
    class Lion extends Cat {
        function __construct($param) {}
    }
    

  2. 覆盖(常规)方法有一个额外的参数。

    class Cat {
        function doSomething() {}
    }
    class Lion extends Cat {
        function doSomething($param) {}
    }
    

第一个会有效,而第二个会抛出Declaration of Lion::doSomething() should be compatible with that of Cat::doSomething()

为什么对构造方法有特殊的态度呢?

admin 更改状态以发布 2023年5月20日
0
0 Comments

里氏替换原则(Liskov Substitution Principle)指出“如果 S 是 T 的子类型,那么类型 T 的对象可以替换为类型 S 的对象”。在你的例子中,这意味着你应该能够用类型为 Cat 的对象替换类型为 Lion 的对象。

这就是为什么你的第二段代码是不允许的。您将不再能够进行此替换,因为您将不再能够调用没有参数的 ->doSomething() 方法。

另一方面,构造函数不受里氏替换原则的约束,因为它不是生成对象的 API 的一部分。无论构造函数签名是否匹配,您仍将能够替换生成的对象。

实际上,子类具有更多构造函数参数是很常见的,因为它们更具体,并且需要更多依赖项。

0
0 Comments

要理解它们为什么被处理不同,你必须理解Liskov替换原则,它表明:

如果对于类型S的每个对象o1都有一个类型T的对象o2,以便对于所有以T为基础定义的程序P,在o1替换o2时,P的行为不变,则S是T的子类型。——Barbara Liskov,《数据抽象与层次结构,SIGPLAN通知,23,5(1988年5月)。

简而言之,这意味着使用你的LionCat的任何类都应该能够可靠地在其上调用doSomething,无论该类是哪一个。如果您更改了方法签名,则不能再保证这一点(尽管您可以扩大它,但不可以缩小它)。

非常简单的例子

public function doSomethingWithFeline(Cat $feline)
{
    $feline->doSomething(42);
}

由于Lion extends Cat,您建立了一种is-a关系,这意味着doSomethingWithFeline将接受一个Lion作为Cat。现在想象一下,在Lion中添加了一个必需的参数到doSomething中。上面的代码将会报错,因为它没有传递那个新参数。因此,需要兼容的签名。

LSP不适用于构造函数因为子类型可能具有不同的依赖关系。例如,如果你有一个FileLogger和一个DBLogger,第一个的构造函数将需要一个文件名,而后者将需要一个db适配器。因此,构造函数是关于具体实现而不是类之间的契约的。

0