你的
Child
类有自己的
x
属性。子类会继承除私有成员外的所有内容,因此所有
public
和
protected
属性/方法都将可用。
你声明了属性
x
,但直到调用
Parent
构造函数之前它才被初始化。如果一个子类(在这种情况下是
Child
)有自己的构造函数,则父构造函数将被覆盖并且不会自动调用。
简而言之:你必须在子类中显式调用父级构造函数:
class Child extends Parent
{
protected $y = 'Some string';
public function __construct()
{
parent::__construct();
var_dump($this->x);
}
}
请注意:如果父类构造函数需要一个参数,那么子类必须做相同的事情(签名必须匹配)。参数类型应该是兼容的(如果不是,则存在违约),你可能希望将参数传递给父类构造函数,让它也执行其任务。
顺便说一句:创建类需要的新实例的构造函数被认为是不良实践。谷歌:
S.O.L.I.D.,特别关注
依赖注入和
Liskov原则,以及
类型提示。如果你阅读了这些材料,你就会明白为什么这是编写代码的更好方式。
class Dad
{
protected $x = null;
public function __construct(Foo $foo)
{
$this->x = $foo;
}
}
class Son extends Dad
{
protected $y = 'Some string';
public function __construct(Foo $foo)
{
parent::__construct($foo);
}
public function test()
{
$results = array();
$results[] = '$this->x instanceof Foo ? '.($this->x instanceof Foo ? 'Of course!': 'No');
$results[] '$this instanceof Son ? '.($this instanceof Son ? 'Yup' : 'No?');
$results[] '$this instanceof Dad ? '.($this instanceof Dad ? 'Yes!' : 'No?');
return $results;
}
}
$son = new Son(new Foo());
echo implode(PHP_EOL, $son->test());
这段代码的输出结果将会是:
$this->x instanceof Foo ? Of Course!
$this instanceof Son ? Yup
$this instanceof Dad ? Yes!
这似乎让许多(相对)新手在面向对象编程方面感到困惑,但是子类与其父类属于同一类型。如果您仔细思考一下,这是有道理的。对于外部世界(即处理/使用给定类的实例的代码),只有公共方法是可见的。根据定义,子类继承了所有公共的东西,因此对于外部世界来说,这并不重要。
如果某些代码需要一个
Dad
实例来做某事,那么一个
Son
也可以工作,因为所有
Dad
提供的功能,
Son
也可以做到。子类唯一要做的就是
增加父类已经提供的功能。
Child
构造函数中调用父类的Parent
构造函数parent::__construct()
。 - Rahil Wazir