当前位置: 首页 > 面试题库 >

覆盖私有方法时的奇怪行为

舒博雅
2023-03-14
问题内容

考虑以下代码:

class foo {
    private function m() {
        echo 'foo->m() ';
    }
    public function call() {
        $this->m();
    }
}

class bar extends foo {
    private function m() {
        echo 'bar->m() ';
    }
    public function callbar() {
        $this->m();
    }
}

$bar = new bar;

$bar->call();
$bar->callbar();

现在,更改m()方法的可见性,我得到:
+for public-for private

Visibility              bar->call()    bar->callbar() 
======================================================
-foo->m(), -bar->m()    foo->m()       bar->m()
-foo->m(), +bar->m()    foo->m()       bar->m()
+foo->m(), -bar->m()    ERROR          ERROR
+foo->m(), +bar->m()    bar->m()       bar->m()

protected看起来像public)。

我期望所有东西都像声明时一样public。但是,尽管foo->call()bar->callbar()本质上是同一件事,但根据m()in
foo和的可见性,它们会产生不同的结果bar。为什么会这样?


问题答案:

继承/覆盖私有方法

在PHP中,子类中的方法(包括私有方法)为:

  • 复制;保留原始功能的范围。
  • 已替换(如果需要,则为“覆盖”)。

您可以使用以下代码查看此内容:

<?php
class A {
    //calling B::h, because static:: resolves to B::
    function callH() { static::h(); }
    private function h() { echo "in A::h"; }
}
class B extends A {
    //not necessary; just to make explicit what's happening
    function callH() { parent::callH(); }
}
$b = new B;
$b->callH();

现在,如果您覆盖私有方法,则它的新作用域将不是A,它将成为B,并且调用将因为A::callH()在scope中运行而失败A

<?php
class A {
    //calling B::h, because static:: resolves to B::
    function callH() { static::h(); }
    private function h() { echo "in A::h"; }
}
class B extends A {
    private function h() { echo "in B::h"; }
}
$b = new B;
$b->callH(); //fatal error; call to private method B::h() from context 'A'

调用方式

这里的规则如下:

  • 查看 对象 实际类 的方法表(在您的情况下为bar)。
    • 如果产生 私有方法
    • 如果定义方法的范围与调用函数的范围相同并且与对象的类相同,请使用它。
    • 否则,请在父类中查找一个私有方法,该私有方法的范围与调用函数之一的范围相同,并且名称相同。
    • 如果找不到满足上述要求之一的方法,则失败。
    • 如果这产生了 公共/受保护的方法
    • 如果方法的范围标记为已更改,则我们可能已用私有/受保护的方法覆盖了私有方法。因此,在这种情况下,如果另外还有一个与调用函数的作用域定义的私有名称相同的方法,请改用该方法。
    • 否则,请使用找到的方法。

结论

  1. (均为私人)对于bar->call(),范围callfoo。调用会$this->m()barfor 的方法表中进行查找m,从而产生一个private bar::m()。但是,的范围bar::m()不同于调用范围foofoo:m()在遍历层次结构时找到该方法,而使用该方法。
  2. (私人foo,公共参与bar)的范围call仍然foo。查找产生公众bar::m()。但是,其作用域被标记为已更改,因此在foomethod 的调用作用域的功能表中进行了查找m()。这将产生一个foo:m()与调用范围具有相同范围的私有方法,因此将其代替。
  3. 在这里什么也看不到,因为可见度降低而出错。
  4. call均为公众)的范围仍然是foo。查找产生公众bar::m()。它的范围未标记为已更改(它们都是公开的),因此bar::m()使用。


 类似资料:
  • 问题内容: 这个想法有些含糊不清,我需要澄清。 我的问题是使用此代码时: 输出为。 这是因为main函数与method在同一类中,还是由于重写? 我已经在书中读过这个想法,当我将该函数放在另一个类中时,会出现编译器错误。 问题答案: 您不能覆盖方法。如果投射到,则看不到。您 可以 覆盖一个方法,但这不是您要在此处执行的操作(是的,在这里,如果将移至,则会得到另一个方法。我建议您在打算覆盖时注解,

  • 问题内容: 在Java中重写私有方法是无效的,因为父类的私有方法是“自动最终的,并且对派生类是隐藏的”。我的问题主要是学术上的。 不允许父级的私有方法被“重写”(即,在子类中以相同的签名独立实现),这是否违反封装规范?根据封装的原理,子类不能访问或继承父级的私有方法。它是隐藏的。 那么,为什么应该限制子类实现自己的具有相同名称/签名的方法呢?这是否有一个良好的理论基础,还是仅仅是某种务实的解决方案

  • 问题内容: 我有这个简单的代码: 我期望它可以打印,但是可以打印。这里发生了什么? 问题答案: 这是因为在循环中,您使用 副本 而不是slice / array元素本身进行操作。 将使得它遍历元素的副本,并追加此一时,循环变量的地址- 这是在所有的迭代相同。因此,您将相同的指针添加3次。而且此临时变量将在最后一次迭代(数组的最后一个元素)中设置为,因此这就是为什么您看到该变量打印了3次的原因。 修

  • 我正在尝试改变鼠标按钮,将地图视图从右键平移到左键。 在单击鼠标左键后,有一个简单的代码来更改按钮: 如何直接改变平移按钮,而不被释放和再次点击?谢谢你的帮助...

  • 通常,具有固定参数数的方法优于具有可变参数数的重载方法。但是,该示例的行为不同: 输出: main的第三行用一个参数调用该方法,该参数是一个包含两个元素的字符串[]。但这并不是用一个参数执行方法,而是执行varargs方法,就像我给了它两个参数一样(这有点正常,因为它是一个数组)。 现在的问题是:这应该发生吗?我是否发现了错误或未记录的行为?它这样做的原因是什么? 我为什么这么做:我想做一个快捷方

  • 问题内容: 我知道我可以使用反射来调用私有方法,并获取或设置私有变量的值,但是我想重写一个方法。 我希望方法 可以打印出来,但是可以打印出来。我听说这可以通过反思来完成,但是我不知道怎么做。如果不是反思,有谁知道另一种方式吗?(除了使方法受保护,或者将方法复制并粘贴到。)如果实际上无法覆盖私有方法,是否可以在其上放置某种触发器以在子类之前或之后调用我的子类中的方法?私有方法执行后? 问题答案: 私