PHP的屬性和方法繼承有什麼奇怪的轉折。請看下面的例子: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類定義,吸氣,返回自己的私人$foo
(= "world"
)。但是,如果我從類B中刪除了getter,而類B繼承了類A的方法,它將返回父類A(= "hello"
)中的私有$foo
屬性。同樣與setters。
我期待繼承的方法在任何時候都可以訪問實例化類的屬性,而不會引用它們的祖先的私有屬性。他們是私人的原因,不是嗎?私人與副作用!如果我將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)
如果我繼承了一個替代這兩種方法,我得到這兩方面"hello"
。但是如果我在B中定義了一個get()
,我分別得到「hello」和「world」。宇宙你好,宇宙呼喚。我需要一些果汁。
編輯對於那些你想知道爲什麼我在這。簡短的背景故事:我正在研究一個在(數組)屬性中更深入生長的類層次結構,每個子類都是相同向量的較不抽象的版本。 (備用你的例子)我正在制定最聰明的方式來合併孩子的擴展屬性和每個連續父母的基礎。
到目前爲止,私有聲明是防止子類覆蓋父類屬性的唯一方法,因爲我可以通過使用父類getter同時獲得屬性。但是,這樣會彌補方法的繼承性,如此處所示。我希望儘可能以自動方式完成此操作;最好用一個單一的clone_parent()
方法繼承並在每個孩子的構造函數中運行。我寧願避免爲每個父 - 子擴展跳都拼寫出單獨的克隆。(我在這裏搜索了一堆,但找到了一個令人滿意的解決方案。)
編輯2:其他方法正在等待我的實際關注(如上所述)導致此實驗。爲了簡單起見,我只是將重疊聲明放在子項的屬性中,並調用一個方法來添加從arent繼承的道具。 (唉,我希望每個擴展類的額外功能都顯示在頂端,而不是隱藏在方法內部的下方!)
無論您是否同意,這是預期的行爲afaik,其原因是因爲它是如何構建的。至於爲什麼它是這樣建造的......呃......誰知道。但對我來說這確實有道理,但也許這只是PHP讓我發瘋。 –
你寫過*好奇* - 這是否意味着私有屬性在其他OO語言(例如C#或Java)中表現不同? –
@JonStirling如果這是它的構建方式,那麼正確的...在我看來,如果你在子類中使用具有相同名稱的私有屬性,繼承會變得複雜得多。我認爲它的簡短版本是**,我們不能繼承訪問私有屬性**的方法。 –