我一直在努力了解类范围如何影响对不同对象的私有属性和受保护属性的访问。当我尝试在父类的上下文中访问子类的私有属性时发现了一些奇怪的东西。
这是代码示例。如果用属性替换方法,则可以看到相同的行为。
class ParentClass{
public function test($childObj){
$childObj->getProtected();
$childObj::getProtected();
$childObj->getPrivate();
$childObj::getPrivate();
}
private function getPrivate(){
echo "ParentClass private";
}
protected function getProtected(){
echo "ParentClass protected";
}
}
class ChildClass extends ParentClass{
private function getPrivate(){
echo "ChildClass private";
}
protected function getProtected(){
echo "ChildClass protected";
}
}
(new ParentClass)->test(new ChildClass());
输出:
ChildClass protected
ChildClass protected (and E_DEPRICATED error)
ParentClass private
Fatal error: Call to private ChildClass::getPrivate() from context 'ParentClass'
嗯,我对前两个输出很好。我认为它在docs中提到过,在父上下文中我可以访问子类的受保护方法/属性。但私人呢?为什么它会在第3个输出中回退到ParentClass方法,并在第4个输出错误?
答案 0 :(得分:3)
这是一个有趣的问题,所以我挖了一个小研究。实际上你做的一些调用是根据文档进行的,但是其他一些调用在现实生活中很少见,而且没有记录,因此我们可以将它们视为PHP实现细节。
首先,您不应该在非静态方法上使用::
运算符,因为PHP通知声明,这是不推荐使用的行为。
因此,让我们将您的测试分成两个单独的测试 - 一个用于非静态方法,另一个用于静态方法。 这是非静态方法测试:
class ParentClass{
public function test($childObj){
$childObj->getProtected();
$childObj->getPrivate();
}
private function getPrivate(){
echo "ParentClass private";
}
protected function getProtected(){
echo "ParentClass protected";
}
}
class ChildClass extends ParentClass{
private function getPrivate(){
echo "ChildClass private";
}
protected function getProtected(){
echo "ChildClass protected";
}
}
(new ParentClass)->test(new ChildClass());
输出:
的相关部分ChildClass受保护
ParentClass private
相同类型的对象可以访问彼此私有和受保护的成员,即使它们不是同一个实例。这是因为在这些对象内部已经知道实现特定的细节。
在第一种情况下,$childObj->getProtected();
- 它起作用,因为$childObj
是ParentClass
的子类型,因此可以将其视为相同类型的对象。
所以我们在这里:
$childObj
变量视为ParentClass
类型getProtected()
方法当我们尝试使用私有方法执行相同的操作时,我们仍然可以调用$childObj->getPrivate()
,但在这种情况下,不应用继承规则,因为私有成员/方法不能通过遗产。所以在这一点上我们是:
$childObj
变量视为ParentClass类型getPrivate()
方法现在,对于静态方法方法,我们调用的是类级方法,而不是实例级方法,因此这里没有适用的继承规则。
我认为,如果我们以这种方式编写静态调用的代码会更清楚(我们不需要对象实例,我们只需要一个类名):
class ParentClass{
public static function test() {
ChildClass::getProtected();
ChildClass::getPrivate();
}
}
class ChildClass extends ParentClass{
private static function getPrivate(){
echo "ChildClass private";
}
protected static function getProtected(){
echo "ChildClass protected";
}
}
(new ParentClass)->test();
输出:
ChildClass受保护
PHP致命错误:未捕获错误:从上下文'ParentClass'调用私有方法ChildClass :: getPrivate()
我认为,这里很明显为什么第二个调用会引发错误 - 我们只是试图调用另一个类的私有staic方法。
为什么第一个调用ChildClass::getProtected()
起作用更有趣,因为我们也试图调用另一个类的受保护方法,并且继承规则不适用于此。
我能找到的唯一解释是,这只是该语言的一个实现细节。 我认为这个受保护的方法调用不应该真正起作用。
我也尝试将其与C ++进行比较,这是我为the first test得到的:
#include <iostream>
using namespace std;
class ParentClass {
public:
void test(ParentClass* obj);
protected:
virtual void getProtected();
private:
virtual void getPrivate();
};
class ChildClass: public ParentClass{
protected:
virtual void getProtected();
private:
virtual void getPrivate();
};
//private virtual
void ParentClass::getPrivate(){
cout << "ParentClass private";
}
//protected virtual
void ParentClass::getProtected(){
cout << "ParentClass protected";
}
//public
void ParentClass::test(ParentClass* obj) {
obj->getProtected();
obj->getPrivate();
};
//private virtual
void ChildClass::getPrivate(){
cout << "ChildClass private";
}
//protected virtual
void ChildClass::getProtected(){
cout << "ChildClass protected";
}
int main() {
cout << "test";
(new ParentClass)->test(new ChildClass);
}
它输出:
测试
ChildClass受保护
ChildClass private
因此它对私有方法的作用与PHP不同,C ++实际上甚至为私有方法调用子类实现。
静态方法的第二个测试:
#include <iostream>
using namespace std;
class ParentClass {
public:
static void test();
};
class ChildClass: public ParentClass{
protected:
static void getProtected();
private:
static void getPrivate();
};
//public static
void ParentClass::test() {
// error: 'static void ChildClass::getProtected()' is protected
//ChildClass::getProtected();
// error: 'static void ChildClass::getPrivate()' is private
//ChildClass::getPrivate();
};
//private static
void ChildClass::getPrivate(){
cout << "ChildClass private";
}
//protected static
void ChildClass::getProtected(){
cout << "ChildClass protected";
}
int main() {
cout << "test";
(new ParentClass)->test();
}
受保护和私人通话都不起作用。你甚至无法使用这些调用编译程序。
我认为这比在PHP中更合乎逻辑,你可以调用受保护的静态方法。