簡體   English   中英

隱藏類的實例變量

[英]Hiding instance variables of a class

我想知道為什么 Java 對具有同名實例變量的超類和子類有這種奇怪的行為。

假設我們有以下類定義:

class Parent {
    int var = 1;
}

class Child extends Parent {
    int var = 2;
}

通過這樣做,我們應該隱藏了超類的變量var 如果我們沒有明確指定通過super調用訪問Parentvar的方法,那么我們將永遠無法從子實例訪問var

但是當我們有一個演員表時,這種隱藏機制就會中斷:

Child child = new Child();
Parent parent = (Parent)child;
System.out.println(parent.var); // prints out 1, instead of 2

這不是完全繞過了字段隱藏的全部要點嗎? 如果是這樣,那么這不是使這個想法完全無用嗎?

編輯:我特別指的是 Java 教程中的這篇文章 它提到

在子類中,超類中的字段不能通過其簡單名稱引用。 相反,該字段必須通過超級訪問...

從我在那里讀到的內容,這似乎暗示 Java 的開發人員在執行此操作時考慮了某種技術。 雖然我同意這是一個相當晦澀的概念,而且一般來說可能是不好的做法。

在 Java 中,數據成員不是多態的。 這意味着Parent.varChild.var是兩個不同的變量,它們碰巧具有相同的名稱。 在派生類中,您在任何意義上都沒有“覆蓋” var 正如您自己發現的那樣,這兩個變量都可以相互獨立地訪問。

最好的前進方式實際上取決於您要實現的目標:

  1. 如果Parent.var不應該對Child可見,請將其設為private
  2. 如果Parent.varChild.var是兩個邏輯上不同的變量,請為它們指定不同的名稱以避免混淆。
  3. 如果Parent.varChild.var在邏輯上是同一個變量,則為它們使用一個數據成員。

字段隱藏的“點”僅僅是指定代碼的行為,該行為確實賦予變量與其超類中的名稱相同的名稱。

它並不意味着用作真正隱藏信息的技術 這是通過將變量設為私有來完成的……我強烈建議在幾乎所有情況下都使用私有變量。 字段是一個實現細節,應該對所有其他代碼隱藏。

這種情況稱為變量隱藏,當子類和父類都有同名變量時,子類的變量隱藏父類的變量,這個過程稱為變量隱藏。

在 Java 中變量不是多態的,變量隱藏與方法覆蓋不同

雖然變量隱藏看起來像覆蓋類似於方法覆蓋的變量,但事實並非如此,覆蓋僅適用於方法,而隱藏適用於變量。

在方法覆蓋的情況下,被覆蓋的方法完全取代了繼承的方法,因此當我們嘗試通過持有子類的對象從父類的引用訪問方法時,會調用子類中的方法。

但是在變量隱藏子類中隱藏了繼承的變量而不是替換,所以當我們試圖通過持有子對象從父類引用訪問變量時,它將從父類訪問。

public static void main(String[] args) throws Exception {

    Parent parent = new Parent();
    parent.printInstanceVariable(); // Output - "Parent`s Instance Variable"
    System.out.println(parent.x); // Output - "Parent`s Instance Variable"

    Child child = new Child();
    child.printInstanceVariable();// Output - "Child`s Instance Variable, Parent`s Instance Variable"
    System.out.println(child.x);// Output - "Child`s Instance Variable"

    parent = child; // Or parent = new Child();
    parent.printInstanceVariable();// Output - "Child`s Instance Variable, Parent`s Instance Variable"
    System.out.println(parent.x);// Output - Parent`s Instance Variable

    // Accessing child's variable from parent's reference by type casting
    System.out.println(((Child) parent).x);// Output - "Child`s Instance Variable"
}

正如我們在上面看到的,當子類中的實例變量與超類中的實例變量同名時,實例變量將從引用類型中選擇。

在 child 和 parent 中聲明具有相同名稱的變量會造成混淆,我們應該始終避免它,這樣就不會有混淆。 這就是為什么我們還應該始終堅持通用指南來創建 POJO並使用私有訪問聲明我們的變量,並提供適當的 get/set 方法來訪問它們。

您可以在我的文章什么是 Java 中的變量陰影和隱藏中閱讀更多內容。

屬性在 Java 中不是多態的,無論如何聲明一個公共屬性並不總是一個好主意。 對於您正在尋找的行為,最好使用私有屬性和訪問器方法,如下所示:

class Parent {

    private int var = 1;

    public int getVar() {
        return var;
    }

    public void setVar(int var) {
        this.var = var;
    }

}

class Child extends Parent {

    private int var = 2;

    public int getVar() {
        return var;
    }

    public void setVar(int var) {
        this.var = var;
    }

}

現在,在測試時,我們得到了想要的結果,2:

Child child = new Child();
Parent parent = (Parent)child;
System.out.println(parent.getVar());

當您進行轉換時,您可以有效地告訴編譯器“我知道得更好”——它暫停了正常的強類型推理規則,並為您提供了懷疑的好處。

通過說Parent parent = (Parent)child; 您告訴編譯器“將此對象視為父對象的實例”。

另一方面,您將 OO 的“信息隱藏”原則(好!)與字段隱藏副作用(通常很糟糕)混淆了。

正如你所指出的:

我們應該隱藏超類的變量 var

這里的要點是變量不會像方法那樣覆蓋,因此當您直接調用Child.var 時,您是直接從 Child 類調用變量,而當您調用Parent.var 時,您是從 Parent 類調用變量,不如果它們確實具有相同的名稱,這很重要。

作為旁注,我會說這真的很令人困惑,不應該被允許作為有效的語法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM