我一直认为我理解OOP是如何工作的(我已经使用它很多年了),但有时我意识到有些概念对我来说仍然不太清楚。
我刚刚遇到一个关于PHP中方法可见性的问题。接受的答案解释了PHP中的子类不能覆盖私有方法。好吧,这很有道理。然而,这个例子让我思考了PHP中的内部继承机制,以及$this
在继承方法上的行为方式。
请考虑以下代码(PHP手册中的示例,也包含在上面提到的问题中):
class Bar
{
public function test() {
$this->testPrivate();
$this->testPublic();
}
public function testPublic() {
echo "Bar::testPublic'n";
}
private function testPrivate() {
echo "Bar::testPrivate'n";
}
}
class Foo extends Bar
{
public function testPublic() {
echo "Foo::testPublic'n";
}
private function testPrivate() {
echo "Foo::testPrivate'n";
}
}
$myFoo = new foo();
$myFoo->test();
/*
Output:
Bar::testPrivate
Foo::testPublic
*/
现在考虑一下PHP手册的摘录:
当从对象上下文中调用方法时,伪变量$this可用$这是对调用对象的引用(通常是该方法所属的对象,但如果从辅助对象的上下文静态调用该方法,则可能是另一个对象)。
解释称"$this
是对调用对象的引用",即$myFoo
。所以我期望$myFoo->test()
总是调用Foo::testPrivate
,而从不调用Bar::testPrivate
(除非$myFoo
是Bar
的实例)。我用get_class
测试了$this
,它总是返回Foo
,即使是从Bar::testPrivate
和Bar::test
内部。然而,当Bar::test
调用$this->testPrivate()
时,$this
的行为类似于Bar
的实例。
这真的很令人困惑,我正试图理解为什么会这样!
我认为继承的方法(public
或protected
)是以某种方式从基类复制到子类的。私有方法根本不会被复制。但这个例子表明它不是这样工作的。看起来Foo
的实例保留了Bar
的内部实例,并在必要时将方法调用委托给它。
我想在这里学到一些东西,只有当事情对我有意义的时候我才会学到。写完这一切之后,我想我可以用两个问题来概括:
有人能简要解释一下继承在PHP内部是如何工作的吗?或者至少给我指一篇关于这方面的文章或文档?
这里讨论的行为或
$this
是否也存在于其他OO语言中,或者它是PHP特有的?
PHP中的继承与大多数面向对象语言中的继承工作方式相同。
当您有一个"虚拟"方法时,该方法不会直接绑定到调用者。相反,每个类都包含一个小的查找表,上面写着"这个方法名绑定到那个实现"。所以,当你说$this->testPublic()
时,实际发生的是PHP:
- 获取当前类的虚拟表
- 在该表中查找
testPublic
的虚拟表条目 - 调用该查找所指向的方法
由于Foo
覆盖testPublic
,因此其虚拟表包含指向Foo::testPublic
的testPublic
条目。
现在,有了私有方法,行为就不一样了。由于正如您正确阅读的那样,私有方法不能被重写,因此调用私有方法永远不会导致虚拟表查找。也就是说,私有方法不能是虚拟的,必须始终在使用它们的类中定义。
因此,效果是在声明时绑定了名称:所有Foo
方法在说$this->testPrivate
时都会调用Foo::testPrivate
,所有Bar
方法都会调用Bar::testPrivate
。
总之,说"继承的方法被复制到孩子身上"是不正确的。实际发生的情况是,子级从用其父类的条目填充其方法名称查找表开始,然后添加自己的函数并替换任何重写的条目。当您调用$this->something
时,会为当前对象的类查询此查找表。因此,如果$this
是Foo
的实例,并且Foo
覆盖testPublic
,则得到Foo::testPublic
。如果$this
是Bar
的一个实例,那么您将得到Bar::testPublic
。
好吧,private
方法和属性正是私有的。对于所有意图和目的,您可以将它们视为"内部",即它们在其中定义的类的内部。这意味着它们永远不会被继承,也永远不会被重写。
因此,当将$this
与private
方法或属性组合使用时,它将始终是与对$this
的引用在同一类中的方法或属性。发生这种情况是因为在父类中调用的$this
无法访问另一个类中的private
方法或属性(因为它们是私有的),甚至从子类访问。
希望这能有所帮助。