子类重写父类变量_为什么在子类中不重写超类的实例变量
子類重寫父類變量
當我們在父類和子類中創建一個具有相同名稱的變量,并嘗試使用持有子類對象的父類引用訪問它時,我們會得到什么?
為了理解這一點,讓我們考慮下面的示例,在該示例中,我們在Parent和Child類中都聲明了一個具有相同名稱的變量x 。
class Parent {// Declaring instance variable by name `x`String x = "Parent`s Instance Variable";public void print() {System.out.println(x);} }class Child extends Parent {// Hiding Parent class's variable `x` by defining a variable in child class with same name.String x = "Child`s Instance Variable";@Overridepublic void print() {System.out.print(x);// If we still want to access variable from super class, we do that by using `super.x`System.out.print(", " + super.x + "\n");} }現在,如果我們嘗試使用以下代碼訪問x ,將打印什么System.out.println(parent.x)
Parent parent = new Child(); System.out.println(parent.x) // Output -- Parent`s Instance Variable通常,我們會說Child類將覆蓋Parent類中聲明的變量,并且parent.x將給我們任何Child's對象所持有的東西。 因為在方法上進行相同類型的操作時發生的是同一件事。
但實際上并非如此, parent.x將為我們提供在Parent類中聲明的Parent實例變量的值,但是為什么呢?
因為Java中的變量不遵循多態性,所以重寫僅適用于方法,而不適用于變量。 并且,當子類中的實例變量與父類中的實例變量具有相同的名稱時,則從引用類型中選擇該實例變量。
在Java中,當我們在Child類中使用已經用于在Parent類中定義變量的名稱定義變量時,Child類的變量將隱藏父類的變量,即使它們的類型不同。 這種概念稱為可變隱藏。
換句話說,當子類和父類都具有相同名稱的變量時,子類的變量將隱藏父類的變量。 您可以在文章什么是Java中的變量陰影和隱藏中閱讀有關變量隱藏的更多信息。
變量隱藏與方法覆蓋不同
盡管變量隱藏看起來像是覆蓋變量,類似于方法覆蓋,但事實并非如此,但覆蓋僅適用于方法,而隱藏適用于變量。
在方法覆蓋的情況下,覆蓋方法完全替換了繼承的方法,因此當我們嘗試通過持有子對象來從父對象的引用訪問該方法時,將調用子類中的方法。 您可以在“方法重載與方法重載”一書中了解有關重載以及被重載的方法如何完全替代繼承的方法的更多信息,以及為什么要遵循方法 重載 規則 。
但是在變量隱藏中,子類隱藏繼承的變量而不是替換它們,這基本上意味著子類的對象包含兩個變量,而子變量則隱藏了父變量。 因此,當我們嘗試從Child類中訪問變量時,將從子類中訪問該變量。
如果我簡化了示例8.3.1.1-3。 隱藏 Java語言規范 的實例變量 :
當我們在Child類中聲明一個具有相同名稱的變量時,例如x作為Parent類中的實例變量,則
為什么以這種方式設計可變隱藏
因此我們知道實例變量是從引用類型而不是實例類型中選擇的,并且多態性不適用于變量,但是真正的問題是為什么? 為什么變量被設計為跟隨隱藏而不是覆蓋。
因為如果我們在子類中更改其類型,則變量覆蓋可能會破壞從父級繼承的方法。
我們知道每個子類都從其父類繼承變量和方法(狀態和行為)。 想象一下,如果Java允許變量覆蓋,并且我們在子類中將變量的類型從int更改為Object 。 它將破壞使用該變量的任何方法,并且由于子代已從父代繼承了這些方法,因此編譯器將在child類中給出錯誤。
例如:
class Parent {int x;public int increment() {return ++x;}public int getX() {return x;} }class Child extends Parent {Object x;// Child is inherting increment(), getX() from Parent and both methods returns an int // But in child class type of x is Object, so increment(), getX() will fail to compile. }如果Child.x覆蓋Parent.x , increment()和getX()工作? 在子類中,這些方法將嘗試返回錯誤類型的字段的值!
如前所述,如果Java允許變量覆蓋,則Child的變量不能替代Parent的變量,這將破壞Liskov替代性原則(LSP)。
為什么從引用類型而不是實例中選擇實例變量
如JVM內部如何處理方法重載和覆蓋中所述 ,在編譯時,覆蓋方法調用僅從引用類處理,但是所有覆蓋的方法在運行時都使用vtable被覆蓋方法替代,這種現象稱為運行時多態性。
同樣,在編譯時也從引用類型處理變量訪問,但是正如我們所討論的那樣,變量不遵循重寫或運行時多態性,因此它們在運行時不會被子類變量替代,仍然引用引用類型。
一般來說,沒有人會建議隱藏字段,因為這會使代碼難以閱讀并造成混亂。 如果我們始終堅持下去,這種混亂就不會出現。
創建POJO并通過聲明為私有字段來封裝我們的字段的一般準則,并根據需要提供getter / setter,以便在該類之外看不到變量,并且子類無法訪問它們。
您可以在此Github存儲庫中找到完整的代碼,請隨時提供寶貴的反饋。
翻譯自: https://www.javacodegeeks.com/2018/11/instance-variable-class-overridden-class.html
子類重寫父類變量
總結
以上是生活随笔為你收集整理的子类重写父类变量_为什么在子类中不重写超类的实例变量的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电脑自带画图工具在哪(电脑里画图工具在哪
- 下一篇: 无叶风扇哪个牌子比较好(性价比高的无叶风