【问题标题】:Strange behavior when overriding private methods覆盖私有方法时的奇怪行为
【发布时间】:2011-04-14 23:18:34
【问题描述】:

考虑以下代码:

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() 方法的可见性,我得到:
+public-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()foobar 中的可见性。为什么会这样?

【问题讨论】:

  • 结果第三行播放不同,我的是foo->m() bar->m(),没有出现错误
  • 为了澄清,这里是您的第三个案例的链接stackoverflow.com/questions/18010637/…
  • foo::call() 和 bar::callbar() 不是一回事。他们有不同的范围。范围定义是否可以调用私有方法。可以这样想:私有方法是类的一个实现细节,所以只有该类内部的代码才能调用它。

标签: php overriding private visibility


【解决方案1】:

继承/覆盖私有方法

在 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() 在作用域 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-&gt;call()call 的范围为foo。调用$this-&gt;m() 会在bar 的方法表中查找m,产生一个私有bar::m()。但是,bar::m() 的作用域与调用作用域不同,后者是foofoo:m() 方法是在向上遍历层次结构时找到的,并被使用。
  2. (私有在foo,公共在barcall的范围仍然是foo。查找产生一个公共bar::m()。但是,它的作用域被标记为已更改,因此在调用作用域foo 的函数表中查找方法m()。这会产生一个私有方法foo:m(),其作用域与调用作用域相同,因此使用它来代替。
  3. 此处没有可看的内容,错误,因为可见性降低了。
  4. (均公开)call 的范围仍为foo。查找产生一个公共bar::m()。它的范围未标记为已更改(它们都是公开的),因此使用了bar::m()

【讨论】:

    【解决方案2】:

    私有方法不可覆盖,因为私有方法即使对其子类也不可见。将方法定义为受保护意味着它在类本身或其子类之外不可见。

    如果您想从父类中使用一个方法,但希望子类能够修改其行为,并且不希望该方法在外部可用,请使用protected。如果您希望父类中的功能不能被子类以任何方式修改,请将方法定义为private

    编辑:进一步澄清,如果您在父类和子类中有两个同名的方法,并且这些方法被定义为私有,则本质上子类方法与父类绝对没有关系方法。如前所述,私有方法对子类是完全不可见的。

    考虑一下:

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

    拨打$bar-&gt;callz();将产生一个错误,因为 z 根本不存在于子类中,甚至作为继承方法也不存在。

    【讨论】:

    • “私有方法不可覆盖”,显然是;如果不是,那为什么bar-&gt;callbar() 会这样呢?
    • m() 中的foopublic,那么bar 可以覆盖m()。否则,bar 不会覆盖任何内容;这就是为什么只有在第 4 种情况下,bar-&gt;call() 的结果是:bar-&gt;m()
    • @NullUserException:当您调用 bar->callbar() 时,它正在调用 bar->m。仅仅因为两个方法具有相同的名称并不意味着其中一个被覆盖(特别是在处理私有方法时)。
    • @NUE:我试图进一步澄清。如果这仍然不能解决您的问题,请告诉我。
    • -1 私有方法对其子类并非完全不可见,它实际上是复制给它们的。所以他们知道他们存在,他们只是不能调用它(错误信息甚至不同)。
    【解决方案3】:

    根据PHP手册:

    声明为私有的成员只能 由定义的类访问 成员。

    http://www.php.net/manual/en/language.oop5.visibility.php

    编辑

    它们会产生不同的结果,具体取决于 关于 m() 在 foo 中的可见性和 酒吧。为什么会这样?

    如果foo 中的m() 是公共的,则它是可覆盖的。在这种情况下,来自barm() 会覆盖foo 中的m()

    【讨论】:

      猜你喜欢
      • 2021-06-09
      • 1970-01-01
      • 2016-09-28
      • 1970-01-01
      • 1970-01-01
      • 2014-05-28
      • 2020-02-15
      • 1970-01-01
      相关资源
      最近更新 更多