簡體   English   中英

Java中的向上渲染是否隱藏了子類方法和字段?

[英]Does up casting in Java hide the subclass methods and fields?

在我正在編寫的程序中,我有一個RestrictedUser類和一個派生自RestrictedUser.User RestrictedUser. 我試圖通過強制轉換為RestrictedUser來隱藏用戶特定的方法,但是當我進行轉換時,User方法仍然可用。 此外,當我運行調試器時,變量的類型將顯示為User

RestrictedUser restricted = regularUser;

在Java中進行強制轉換是否隱藏了子類方法和字段,或者我做錯了什么? 有解決方法嗎?

謝謝

如果您嘗試運行此代碼:

User user = new User(...);
RestrictedUser restricted = user;
restricted.methodDefinedInTheUserClass(); // <--

你會得到一個編譯錯誤。 這在任何方面,形狀或形式都不會是安全的,因為即使您將RestrictedUser傳遞給另一種方法,該方法也可以這樣做:

if (restricted instanceof User) {
    User realUser = (User)restricted;
    realUser.methodDefinedInTheUserClass(); // !!
}

而且你的“受限制”用戶不再受限制了。

該對象在調試器中顯示為User對象,因為它是User對象,即使對象引用存儲在RestrictedUser變量中也是如此。 基本上,將子類的實例放在具有父類類型的變量中確實會“隱藏”子類的方法和字段,但不安全。

我不確定你問的是什么,因為你使用的術語有點不清楚但是這里有。 如果你有一個超類和一個子類,你可以通過將它們設為私有來隱藏子類中超類的方法。 如果你需要超類上的公共方法是隱形的,那你就不走運了。 如果將子類強制轉換為超類,則子類上的公共方法將不再可見。

可能對您有幫助的一個建議是使用組合而不是繼承,將敏感方法提取到單獨的類中,然后根據需要將該類的適當實例插入到對象中。 如果需要,可以將類中的方法委托給注入的類。

你會混淆靜態類型和動態類型。

靜態類型是引用的類型。 當您從Derived轉發到Base時,您告訴編譯器,只要您知道,指向的東西就是Base。 也就是說,你承諾指向的對象是null,或Base,或從Base派生的東西。 也就是說,它有Base的公共接口。

您將無法調用Derived中聲明的方法(而不是Base中),但是當您調用Derived重寫的任何Base方法時,您將獲得Derived的重寫版本。

如果需要保護子類,可以使用Delegate模式:

public class Protect extends Superclass {  // better implement an interface here
  private final Subclass subclass;

  public Protect(Subclass subclass) {
    this.subclass = subclass;
  }

  public void openMethod1(args) {
    this.subclass.openMethod1(args);
  }

  public int openMethod2(args) {
    return this.subclass.openMethod2(args);
  }
  ...
}

您還可以考慮使用java.lang.reflect.Proxy
[]]

Peter的答案John的答案是正確的:如果對象的真實類型(客戶端代碼可以轉換為,調用反射方法等)仍然可用,那么僅僅轉換靜態類型將不起作用。 你必須以某種方式掩蓋真實的類型(正如彼得的回答所提到的)。

實際上有一種模式:如果您有權訪問JDK源代碼,請查看Executors類中的Executors unconfigurable*方法。

Java的

在Java中,您永遠不能從對象中“隱藏”某些東西。 編譯器可以忘記您對特定實例的詳細了解。 (就像它的子類一樣)調試器比編譯器知道得多,所以它會告訴你當前實例的實際類型,這可能就是你所經歷的。

OOP

聽起來你正在編寫邏輯,需要知道你正在使用哪種類型的對象,這在OOP中是不推薦的,但是出於實際原因通常需要這樣做。

限制形容詞

正如我在對這個問題的評論中所說,你應該清楚這里的基類是什么。 直覺RestrictedUser應該是User的子類,因為名稱提示更專業的類型。 (名稱上有一個額外的主動形容詞。)在你的情況下這是特殊的,因為這是一個限制形容詞,它可以讓你把User作為RestrictedUser的子類完全沒問題。 我建議將這兩個重命名為:BasicUser / UserWithId和NonRestrictedUser,以避免限制形容詞,這是令人困惑的部分。

此外,不要試圖認為你可以隱藏匿名類中的方法。 例如,這不會提供您期望的隱私:

Runnable run = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello, world!");
    }

    public void secretSquirrel() {
        System.out.println("Surprise!");
    }
}

您可能無法命名實際類型的run (為了直接轉換為它),但您仍然可以使用getClass()獲取其類,並且可以使用反射調用secretSquirrel (即使secretSquirrel是私有的,如果你沒有SecurityManager ,或者它被設置為允許可訪問性,反射也可以調用私有方法。)

我認為你可能做出了一個糟糕的命名選擇:我認為RestrictedUser將是User的子類,而不是相反,所以我將使用UnrestrictedUser作為子類。

如果將UnrestrictedUser向上轉換為RestrictedUser ,則您的引用只能訪問RestrictedUser聲明的方法。 但是,如果您將其轉發回UnrestrictedUser ,您將可以訪問所有方法。 由於Java對象知道它們究竟是什么類型,因此無論您使用何種類型的引用(甚至是Object ),任何實際上是UnrestrictedUser東西都可以隨時返回到它。 這是不可避免的,也是語言的一部分。 但是,您可以將其隱藏在某種代理之后,但在您的情況下可能會破壞目的。

此外,在Java中,默認情況下所有非靜態方法都是虛擬的。 這意味着如果UnrestrictedUser覆蓋RestrictedUser聲明的某些方法,則即使您通過RestrictedUser引用訪問它,也將使用UnrestrictedUser版本。 如果需要調用父類版本,可以通過調用RestrictedUser.variableName.someMethod()進行非虛擬調用。 但是,您可能不應經常使用它(最少的原因是它破壞了封裝)。

我也認為這個問題引出了另一個問題。 你打算怎么稱呼這種方法? 似乎有一個Class Heirarchy,其中子類化引入新方法簽名將需要在調用者中進行instanceof檢查。

您是否可以更新您的問題以包含您將調用這些方法的方案? 也許我們能夠提供更多幫助。

John Calsbeek給出了技術答案。 我想考慮一下設計問題。 您要做的是阻止某些代碼段或某些開發人員了解有關User類的任何信息。 您希望他們將所有用戶視為RestrictedUsers。

你這樣做的方式是使用權限。 您需要阻止有問題的開發人員訪問User類。 您可以通過將其放在包中並限制對包的訪問來實現。

暫無
暫無

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

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