天天看點

《通過實驗看 Java 中私有成員是否能被繼承》續

我在蔽文《通過實驗看 Java 中私有成員是否能被繼承》中所舉的例子有一個錯誤,使用了屬性而不是方法來驗證私有成員是否能夠被子類所繼承。在 Java Programming Language 中,有這麼一段:“字段(field)隻能被隐藏,而不能被覆寫。如果在子類中聲明了一個和父類中相同名字的字段,那麼父類中的字段仍然存在,但是不能再簡單的使用其名字來通路父類中這個被隐藏的字段。”是以,在上文所涉及的實驗中,使用同名屬性來驗證子類是否能繼承父類中的私有成員就是錯誤的。

有一個問題是,ChildrenClass 毫無疑問是繼承了 ParentClass 中的 getMsg 方法的,但是為什麼向 c 對象的發送 getMsg 消息,傳回的卻還是 ParentClass 中的屬性呢?Java Programming Language 解釋道,一個方法通路一個被子類重新定義的成員的時候,需要根據被通路成員的種類、通路限定符和如何引用該成員等方面來決定該方法會通路哪一個成員(父類中的那個還是子類中的那個)。當通過對象引用來調用一個方法的時候,那麼對象的實際的類将決定使用哪一個實作(the actual class of the object governs which implementation is used);當通路一個字段的時候,引用的聲明類型将會被使用(the declared type of the reference is used)。具體到上文中的實驗代碼來說,getMsg 方法将總是使用和它在同一個類中聲明的那個字段,也就是雖然 ParentClass 中的 msg 屬性被 ChildrenClass 中的 msg 給隐藏了,但是 getMsg 方法是在 ParentClass 中被聲明的,而且在 ChildrenClass 中沒有被覆寫掉,那麼我們向 c 對象發出 getMsg 消息,實際上 getMsg 方法将仍然通路和它在同一類中聲明的 msg 屬性。

接着我們回歸前文的目的,繼續考察子類是否能夠繼承父類中的私有成員。我們通過上面的讨論,可以知道私有屬性是被保留在父類中的,而不會被子類繼承。接着我們看看私有方法的命運。我修改了以前的實驗代碼,如下:

package com.patrickhe.test.inheritprivate;

public class ParentClass {

    private String msg() {

       return "I am an attribute in ParentClass.";

    }

    public String getMsg() {

       return msg();

    }

}

package com.patrickhe.test.inheritprivate;

public class ChildrenClass extends ParentClass{

    private String msg() {

       return "I am an attribute in ChildrenClass.";

    }

}

測試類的代碼保持不變。運作測試類,可以看到控制台的輸出結果為:

ParentClass object outputs:

I am an attribute in ParentClass.

ChildrenClass object outputs:

I am an attribute in ParentClass.

如果将 ParentClass 和 ChildrenClass 中 msg 方法的通路限定符改為 public,再運作測試類,我們可以看到輸出結果變為:

ParentClass object outputs:

I am an attribute in ParentClass.

ChildrenClass object outputs:

I am an attribute in ChildrenClass.

我們知道,在 Java 中 可以通過在子類中聲明具有相同名字相同簽名的方法來覆寫掉父類中的相應方法。具體讨論私有方法的繼承問題,如果說子類是可以繼承父類的私有方法的話,那麼 子類也就能夠覆寫掉父類中的私有方法;反之,如果子類覆寫不了父類中的私有方法的話,也就是說子類不能從父類繼承其私有方法。在實驗結果中,我們看到了子 類是不能覆寫掉父類中的私有方法的,也就意味着子類沒有從父類繼承私有方法,但是子類可以繼承父類的非私有方法。最後引述一段 Java Programming Language 中的話:“方 法隻有在它可以被通路的時候才能被覆寫。如果一個方法不能被通路的話,也就是說這個方法不能被繼承,它既然不能被繼承的話那麼也就不能被覆寫。例如,私有 方法在它所在類之外是不能被通路的。如果子類中碰巧定義了一個方法和父類中的私有方法具有完全相同名字、簽名和傳回值類型,那麼這兩個方法是完全沒有任何 聯系的——子類中的方法将不會覆寫父類中的方法”。