在PHP中处理继承时,我发现一些知识缺乏,主要是关于构造函数和私有属性。
让我们以这个代码为例:
<?php
class Module
{
public $type;
public function __construct($type)
{
$this->type = $type;
}
}
class BModule extends Module
{
}
class CModule extends BModule
{
}
class A
{
private $module;
public function __construct(Module $module)
{
echo 'Set module for '.__CLASS__.' to '.$module->type . PHP_EOL;
echo "<br>";
$this->module = $module;
}
public function getModule()
{
echo "I (as " . __CLASS__ . ") have a module of type " . $this->module->type;
return $this->module->type;
}
}
class B extends A
{
}
$m = new Module('base-module');
$bm = new BModule('bi-module');
echo "<br>--------A---------<br>";
$a = new A($m);
echo "<br>A is of type " . $a->getModule();
echo "<br>--------B---------<br>";
$b = new B($bm);
echo "<br>B is of type " . $b->getModule();
一些问题:
- B构造不应该在B的上下文中调用构造函数吗?(所以我预计它会失败,因为它没有继承私有属性$module)
- 或者PHP会简单地调用A构造函数,使用/引用A中的方法和属性?(包括私有)
- 我可以将Module或BModule对象传递给$b;这是因为BModule是Module的子级。在验证类型提示时,PHP是否正在检查传递对象的继承链(检查父对象)?
- 因此,我可以向构造函数传递Module或BModule类型的对象吗
这是另一个例子:
<?php
class a
{
private $a;
protected $a_copy;
public function __construct($a_value)
{
$this->a = $a_value;
$this->a_copy = $this->a;
}
public function getA()
{
return $this->a;
}
public function getCopyA()
{
return $this->a;
}
}
class b extends a
{
}
$a = new a('value for a');
$b = new b('value for b');
echo "<br>-----A-----<br>";
echo $a->getA()."<br>";
echo $a->getCopyA()."<br>";
echo "<br>-----B-----<br>";
echo $b->getA()." (I would expect to have no access to '$a)<br>";
echo $b->getCopyA()."<br>";
作为属性$a私有,我将无法访问它或从类b中对它做任何事情。对我的实际理解来说,这有点不合理。
这是预期的功能,尽管B继承了A的所有方法,但它们不是在B的上下文中调用的,而是在A的上下文中进行调用的。因此调用了A构造函数。这意味着,即使对象被扩展,在A中定义的函数也可以访问A的属性。但是,在A中定义的方法不能访问B的属性,这似乎是您的理解。
因此,很快回答您的问题:
- 不,函数总是在定义的上下文中调用,而不是从调用它们的位置调用
- PHP将检查继承链的所有路径,看看它是否正确。类的任何子级都可以被假定具有相同的函数。因此,如果B扩展了A,当它的类型被提示为A时,你可以使用B或A作为参数,但只在它的类型提示为B时使用B
Ad 1:否,被调用方法的上下文是声明方法(在本例中为构造函数)的地方。如果上下文是类B
,那么任何人都可以通过扩展它来破坏你的类
看看这个例子:
class A
{
private $module;
public function __construct(Module $module)
{
echo 'Set module for '.__CLASS__.' to '.$module->type . PHP_EOL;
echo "<br>";
$this->module = $module;
}
}
class B extends A
{
public function __construct()
{
parent::__construct(new Module()); // call the parent (which is A)
}
}
这说明了A::__construct()
的作用域实际上是A
类。
广告2:是的,每个作为子类实例的对象都可以用来代替超级类。这就是为什么您应该编写类,以便在静态类型需要一个超级类时可以替换它们。有关此主题的更多信息,请参阅利斯科夫替代原理。
至于最后一个例子:同样,子类中没有任何代码可以对超级类的私有成员进行操作。所有代码都在超类上下文中操作。所以这里没有问题。
如果你试图重载超类的方法并像这样使用它的私有成员,就会出现问题:
class b extends a
{
public function getA()
{
return $this->a . "_suffix"; // error
}
}
在这种情况下,您必须依赖于超类中getA()
方法的实现:
class b extends a
{
public function getA()
{
return parent::getA() . "_suffix"; // ok, we are depending on the super class implementation
}
}