【发布时间】:2016-11-14 19:13:28
【问题描述】:
PHP 的属性和方法继承中的一个奇怪的转折。请参阅以下示例:
class A {
private $foo = 'hello';
function get() {
return $this->foo;
}
function set($val) {
$this->foo = $val;
}
}
class B extends A {
private $foo = 'world';
function get() { // try inheriting this
return $this->foo;
}
function set($val) { // try inheriting this
$this->foo = $val;
}
}
现在让我们看看如果我们继承或定义方法会产生怎样的影响:
$b = new B();
/* If B defines getter: */
echo $b->get(); // 'world'
/* If B inherits getter: */
echo $b->get(); // 'hello'
// Enter Setter!
$b->set('cosmos');
/* If B defines setter & inherits getter: */
echo $b->get(); // 'hello'
/* If B inherits setter & defines getter: */
echo $b->get(); // 'world'
/* If B defines or inherits both: */
echo $b->get(); // 'cosmos'
所以:当 B 类定义 getter 时,它返回自己的私有 $foo (= "world")。但是,如果我从 B 类中删除 getter,并且 B 类从 A 类继承了完全相同的方法,它会改为从父类 A (= "hello") 返回私有 $foo 属性。二传手也是如此。
我期望继承的方法始终访问实例化类的属性,而不引用其祖先的私有属性。他们是私人的,不是吗?私人有副作用!如果我将 prop 可见性更改为 public 或 protected,则返回类 B 中的预期属性“world”和“cosmos”,无论方法是定义还是继承。
以下是上述每种情况的对象转储:
var_dump($b);
/* If B defines getter (Prop get from B): */
["foo":"B":private] · string(5) "world"
["foo":"A":private] · string(5) "hello"
/* If B inherits getter: (Prop get from A) */
["foo":"B":private] · string(5) "world"
["foo":"A":private] · string(5) "hello"
// Enter Setter!
$b->set('cosmos');
/* If B defines setter & inherits getter
* Prop set to B & get from A */
["foo":"B":private] · string(6) "cosmos"
["foo":"A":private] · string(5) "hello"
/* If B defines getter & inherits setter
* :: Prop set to A & get from B */
["foo":"B":private] · string(5) "world"
["foo":"A":private] · string(6) "cosmos"
/* If B defines both:
* :: Prop set to B & get from B */
["foo":"B":private] · string(6) "cosmos"
["foo":"A":private] · string(5) "hello"
/* If B inherits both:
* :: Prop set to A & get from A: */
["foo":"B":private] · string(5) "world"
["foo":"A":private] · string(6) "cosmos"
有人可以向我解释发生了什么的逻辑吗?继承的属性访问方法是否绑定到原始方法定义父类的私有属性?我不想将相同的方法复制粘贴到每个具有匹配名称的私有属性的子类中。
如果上面的插图令人作呕,请见谅。为了我自己的理智,不得不把它拼出来——出乎意料的行为,除非我遗漏了一些明显的东西。 (注意,在子类构造函数中拥有构造函数或调用 parent::__construct() 对此行为没有影响。)
添加:现在更进一步。如果我声明 getter 受保护,并在 B 类中创建一个公共方法来获取变量。像这样:
class B {...
public function getpro_direct() {
return $this->foo;
}
public function getpro_getter() {
return $this->get();
}
...}
echo $b->getpro_direct(); // "world" (from B)
echo $b->getpro_getter(); // "hello" (from A)
如果我从 A 继承这两种方法,我会在这两个方面都得到 "hello"。但是如果我在 B 中定义一个get(),我会分别得到“hello”和“world”。你好世界你好,宇宙在召唤。我需要一些果汁。
编辑,对于那些想知道我为什么要这样做的人。简短的背景故事:我正在研究一个在(数组)属性中更深入地增长的类层次结构,每个子类都是同一个向量下的一个不太抽象的版本。 (省去例子。)我正在研究将孩子的扩展属性与每个连续父母的基础合并的最聪明的方法。
到目前为止,私有声明是防止子类覆盖父类属性的唯一方法,因为我可以通过使用父 getter 同时获得两者的属性。然而,这会破坏方法继承,如此处所示。我想尽可能自动地完成这项工作;最好使用单个clone_parent() 方法,该方法继承并在每个子构造函数中运行。我宁愿避免为每个父子扩展跃点拼写单独的克隆器。 (我在这里搜索和阅读了一堆,但没有找到令人满意的解决方案。)
Edit2:其他方法待定,我实际担心(如上所述)导致了这个实验。为了简单起见,我将放弃子属性中的重叠声明,并调用一个 extend_props 方法,该方法在从 arent 继承的 props 上添加。 (唉,我希望每个扩展类的附加功能都显示在上面,而不是潜伏在方法的下面!)
【问题讨论】:
-
无论您是否同意,这都是 afaik 的预期行为,其原因是因为它就是这样构建的。至于为什么会这样建造……嗯……谁知道呢。但对我来说它确实有道理,但也许这只是 PHP 让我发疯。
-
您写道它是好奇 - 这是否意味着私有属性在其他 OO 语言(如 C# 或 Java)中的行为不同?
-
@JonStirling 如果这是它的构建方式,好吧...在我看来,如果您在子类中使用具有相同名称的私有属性,它会使继承变得更加复杂。在我看来,短版是,我们不能继承访问私有属性的方法。
-
@N.B.我不知道这在其他语言中是如何工作的。我很好奇,因为它会迫使我在子类中重新定义相同的方法,这似乎是代码的冗余重复。特别是如果相同的类模式被继承了几代。我想访问父级的私有属性的需要比继承访问属性定义类的私有属性的方法的需要少。
-
如果你做了一些东西
private那么这意味着你了解它在继承过程中的表现。在为某些东西开发接口时,有时您希望禁止子类修改核心功能,因此您创建属性private并创建可以更改或访问这些属性的方法。这样做,您将强制所有内容“通过”一组方法,这些方法可以在更改您认为必要的内容之前实现所需的检查。这是正常行为,我在这里闻到了 XY 问题。
标签: php oop inheritance properties parent-child