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

时间:2013-03-14 15:45:36

标签: php oop constructor

以下是几个片段:

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

    class Cat {
        function __construct() {}
    }
    
    class Lion extends Cat {
        function __construct($param) {}
    }
    
  2. 覆盖(常规)方法有一个额外的参数。

    class Cat {
        function doSomething() {}
    }
    
    class Lion extends Cat {
        function doSomething($param) {}
    }
    
  3. 第一个会起作用,而第二个会抛出Declaration of Lion::doSomething() should be compatible with that of Cat::doSomething()

    为什么对构造函数方法有特殊态度?

5 个答案:

答案 0 :(得分:34)

要理解为什么对待它们的方式不同,您必须了解Liskov's Substitution Principlestates

  

如果对于类型S的每个对象o1,存在类型为T的对象o2,使得对于根据T定义的所有程序P,当o1代替o2时P的行为不变,则S是T的子类型。“ - BarbaraLiskov,数据抽象和层次结构,SIGPLAN通知,23,5(1988年5月)。

简而言之,这意味着使用LionCat的任何班级都应该可以在其上可靠地调用doSomething,无论班级是一个还是另一个。如果您更改方法签名,则不再保证(您可以扩展它,但不能缩小它)。

非常简单的例子

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

Lion extends Cat以来,您建立了is-a关系,这意味着doSomethingWithFeline将接受Lion Cat。现在假设您在doSomething中向Lion添加了必需的参数。上面的代码会破坏,因为它没有传递新的参数。因此,需要兼容的签名。

LSP does not apply to constructorsbecause subtypes might have different dependencies。例如,如果你有一个FileLogger和一个DBLogger,第一个的ctors(构造函数)需要一个文件名,而后一个需要一个db适配器。因此,ctors是关于具体的实现,而不是类之间的契约的一部分。

答案 1 :(得分:12)

Liskov Substitution Principle表示“如果S是T的子类型,则T类型的对象可以用类型S的对象替换”。在您的示例中,这意味着您应该能够使用Cat类型的对象替换Lion类型的对象。

这是您不允许使用第二个代码的原因。您将无法再进行此替换,因为您无法再使用参数调用->doSomething()方法。

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

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

答案 2 :(得分:5)

__construct(),因为它们是每个班级唯一的。 Lion的构造函数与Cat的构造函数不同,但如果Lion扩展CatLion没有__construct(),则仍然可以使用__construct()扩展父Lion::__construct()

  

与其他方法不同,PHP不会生成E_STRICT级别   __construct()被不同覆盖时的错误消息   参数比父__construct()方法有。

     

PHP Manual: Constructors and Destructors

其他魔术方法采用特定的参数,这意味着他们的参数计数等将始终是一致的。

在实例化类之后,然后为doSomething()执行多态/重写。您的父类与抽象类一样,定义了子类需要匹配的参数和可见性,以支持覆盖。

答案 3 :(得分:2)

构造函数仅用于具体对象。它诞生了它。

其他方法是 - 只要涉及子类型 - 与继承类有关,因此这些方法是同一对象的一部分,因此在分布在多个类的定义时应该是兼容的。

否则你创建一个有点shizo的对象。


编辑: 如果您还想对构造函数签名引入严格检查,那么您可以使用interface,因为PHP 5.2添加了构造函数检查:

  

在接口中添加了对构造函数的支持,以强制实现中的构造函数签名检查。从PHP 5.2.0开始,接口可以有构造函数。但是,如果您选择在接口中声明构造函数,则实现该接口的每个类必须包含一个构造函数,其签名与基本接口构造函数的签名相匹配。 “签名”是指参数和返回类型定义,包括任何类型提示,包括数据是通过引用还是通过值传递。

(取自:Other Enhancements - Migrating from PHP 5.1.x to PHP 5.2.x

答案 4 :(得分:1)

您所描述的是overloading,PHP不支持。现在,当您为类创建构造函数时,它仅在该类中使用,并且默认情况下不调用父构造函数(请参阅Constructors and Destructors。您需要手动调用它parent::__construct()

在类中创建方法时,它会直接访问父方法。

总之:

  • 构造函数不使用重载但仅适用于类本身
  • 该方法使用重载,因此不允许