【问题标题】:public/protected class properties don't override private properties on parent classes?公共/受保护的类属性不会覆盖父类的私有属性?
【发布时间】:2018-01-18 21:01:10
【问题描述】:

在 Parent 类上声明一个私有属性,然后在 Child 类上将该属性重新声明为 public 或 protected。

当您创建 Child 类的实例并调用从 Parent 类继承的方法时。使用的是 Parent 类的属性,而不是 Child 类的属性。

如果父类上的属性的初始声明是公共的或受保护的,则情况并非如此。

谁能解释一下这是为什么?

<?php

class P {
    private $foo = 'bar';

    public function e()
    {
        echo get_class($this);
        echo "\n";
        echo $this->foo;
        echo "\n";
    }
}

class C extends P
{
    protected $foo = 'baz';
}

$parent = new P();
$child = new C();

$parent->e();
$child->e();

Output:
P
bar
C
bar

Expected Output:
P
bar
C
baz

【问题讨论】:

  • private 是私有的,如果您想允许更改,请在父类中使用protected。 RTM:php.net/manual/en/language.oop5.visibility.php 或使用 setter。
  • 这是预期的行为。 private properties 仅在定义它的类的范围内可见。您可以认为它在任何子类中都不存在,因此具有相同名称的东西不会覆盖它,而只是创建一个子范围内恰好具有相同名称的新属性。
  • 这里有很好的解释。 stackoverflow.com/questions/4361553/…
  • 我喜欢这个问题,我不明白为什么它被否决:它简洁,清楚地说明了 OP 遇到问题的区域,似乎不是重复的(至少在第一眼)并且只包含重现问题的必要代码。

标签: php


【解决方案1】:

私有属性,顾名思义,就是私有的。这意味着它们只能从类中访问。正如其他人在 cmets 中提到的那样,this question 对 PHP 中的变量作用域提供了非常好的深入解释,所以一定要去看看,这是一本有趣的读物。

然而,在您的具体情况中,您似乎担心重命名/重载子类中的变量。更具体地说,我们正在研究当父类和子类碰巧都具有相同名称的属性并且父类具有返回该属性的函数时会发生什么。

class A {
    private $foo = 'bar';

    public function getFoo() {
        return $this->foo;
    }
}

class B extends A {
    protected $foo = 'something something';
}

$b = new B;

echo $b->getFoo();

在上面的示例中,输出将是bar,这实际上是预期的行为。由于函数getFoo在子类中没有被覆盖,所以PHP在父类中执行该函数。当它到达那里时,$this 引用A,因此打印$foo 的值,正如它在A 中定义的那样。有趣的是,在这个特定示例中,B::foo 的范围并不重要。事实上,它可以完全省略,代码仍然可以正常运行。

我们可以试验不同的函数作用域,看看会发生什么:

class A {
    private $foo = 'bar';

    public function getFoo() {
        return $this->foo;
    }

    private function getCapitalizedFoo() {
        return strtoupper($this->foo);
    }

    protected function getDoubleFoo() {
        return $this->foo . $this->foo;
    }
}

class B extends A {
    protected $foo = 'something something';

    public function getParentDoubleFoo() {
        return parent::getDoubleFoo();
    }
}

$b = new B;

echo $b->getFoo().PHP_EOL;      // Output: bar
echo $b->getParentDoubleFoo();  // Output: barbar
echo $b->getDoubleFoo();        // Output: Uncaught Error: Call to protected method ...
echo $b->getCapitalizedFoo();   // Output: Uncaught Error: Call to private method ...

现在,问题仍然存在:如何从父类访问私有成员?

如果你读过这个主题,你可能知道,除非完全必要,你的对象的属性应该是私有的,以便尽可能地封装它的逻辑和数据。知道这一点,允许子类访问和修改其父类属性的最佳方法是为它们创建访问器和修改器。

class A {
    private $foo = 'foo';
    private $bar = 'bar';

    public function setFoo($foo) {
        $this->foo = $foo;
    }

    public function getFoo() {
        return $this->foo;
    }

    protected function setBar($bar) {
        $this->bar = $bar;
    }

    protected function getBar() {
        return $this->bar;
    }
}

class B extends A {
    public function __construct() {
        parent::setFoo('more foo');
        parent::setBar('more bar');
    }

    public function getCapitalizedFoo() {
        return strtoupper(parent::getFoo());
    }

    public function getCapitalizedBar() {
        return strtoupper(parent::getBar());
    }
}

$b = new B;

echo $b->getCapitalizedFoo();    // Output: MORE FOO
echo strtoupper($b->getFoo());   // Output: MORE FOO
echo $b->getCapitalizedBar();    // Output: MORE BAR
echo strtoupper($b->getBar());   // Output: Uncaught Error: Call to protected method ...

通过创建公共函数来访问和修改父类的属性,您可以让子类覆盖它们并执行自己的逻辑,而无需复制变量。然而,这也意味着B 的任何实例都可能改变A 中定义的值。为了缓解这种情况,您可以创建受保护的函数,允许 B 在内部访问和使用父类属性,但使用 B 的代码将无法执行相同操作。

【讨论】:

  • 好答案,除了 $this 指的是A。这是不正确的。
  • @deceze 我刚刚用 PHP8 重新测试只是为了确保它仍然正确并且它确实按照我在原始答案中描述的方式工作。
猜你喜欢
  • 2013-10-10
  • 2023-03-04
  • 2011-10-11
  • 2016-08-13
  • 1970-01-01
  • 2012-01-31
  • 2014-04-30
  • 2023-04-08
  • 1970-01-01
相关资源
最近更新 更多