2009-12-14 39 views
11

我有一個類結構,我希望基類中的某些方法可以從直接從基類派生的類訪問,但不能派生自派生類。根據Java語言規範,可以覆蓋繼承方法的訪問規範,使它們更公開,但不是更私密。例如,這是我需要做的要點,但是非法:是否可以在Java中隱藏或降低對繼承方法的訪問?

// Defines myMethod 
public class Base { 
    protected void myMethod() {} 
} 

// Uses myMethod and then hides it. 
public class DerivedOne extends Base { 
    @Override 
    private void myMethod(); 
} 

// can't access myMethod. 
public class DerivedTwo extends DerivedOne { 

} 

有什麼辦法可以做到這一點嗎?

編輯解釋爲什麼我想這樣做:

在這種情況下,類結構是數據處理和進口結構。它讀入並分析充滿表格數據的文本文件,然後將它們存儲在數據庫中。

基類是管理數據庫處理部分的基表類。其中包含相當數量的功能,這對所有表類型都很常見 - 因爲一旦它們在數據庫中,它們就會變得統一。

中間類是特定於正在解析的文件中的表的類型,並具有表解析和導入邏輯。它需要訪問一些基類的數據庫訪問函數。

頂級類是特定於表的,只不過是以父類可以理解的方式初始化表的佈局。此外,基類的用戶不需要查看或訪問中間類所具有的數據庫特定功能。實質上,我想把這些函數只展現在基類之上的一層,而不是其他人。

我問,因爲雖然我作爲例子發佈的代碼是非法的,但可能還有其他一些手段來完成相同的目的。我問是否有。

也許隱藏是一種錯誤的方式來描述這個 - 我真正需要做的是將一些應該是基類私有的功能公開到層次結構中上一級的類中。隱藏會做到這一點 - 但我可以看到隱藏是一個問題。有沒有另一種方法來做到這一點?

+2

由於DerivedOne是一個基地,它必須容納基地的合同。順便說一下,這就是爲什麼委託(複合模式)通常比繼承更受歡迎的原因。 – 2009-12-14 18:03:31

+0

@Steve我不相信複合模式可以在這裏工作。從本質上講,我只需要將一些對基類應該是私有的功能展示給一個級別的類。 – 2009-12-14 18:33:39

+0

更新了我的答案。 – rsp 2009-12-14 19:56:27

回答

15

我認爲這個問題的本質爲你帶來它暴露了你的對象模型的概念問題。你試圖將各種單獨的責任描述爲「是」的關係,而實際上你應該做的是描述「擁有」或「使用」關係。事實上,你想從一個子類隱藏基類功能告訴我這個問題實際上並沒有映射到一個三層繼承樹上。

這聽起來像你正在描述一個經典的ORM問題。讓我們再看看這個,看看我們是否可以把它重新映射到比嚴格其他概念「是一個」繼承,因爲我真的認爲你的問題不是技術問題,它的概念:

你說:

的基類是基表類 管理數據庫處理的 部分它。有包含在它的 功能有相當數量是共同 所有表類型 - 因爲一旦 他們在數據庫中,他們成爲 均勻。

這可能更清晰,但它聽起來像我們有一個類需要管理數據庫連接和常見數據庫操作。在Single Responsibility之後,我想我們已經完成了。你並不需要擴展這個類,你需要它需要使用它的功能的類。

中產階級是特定的文件是 解析在 樣的表,並具有表解析和 進口邏輯。它需要訪問基類的數據庫訪問 函數的一些 。

這裏的「中產階級」聽起來有點像Data Mapper。這個類不需要擴展上一個類,它需要擁有對它的引用,或許注入構造函數或setter作爲接口。

頂層類是特定於 表,並確實沒有什麼比 更在某種程度上 父類可以理解初始化表的佈局。 此外,用戶還基類的不 需要看或訪問哪些中間 類做數據庫 特定功能。實質上,我想揭示 這些函數只能在基類之上的一個級別 而沒有其他人。

我不清楚爲什麼一個高層次的類似乎有關於db模式的知識(至少這就是「初始化表格佈局」這個詞給我的建議),但是,如果前兩節課是encapsulation ("has a"/"uses a") instead of inheritance ("is a"),我不認爲這會是一個問題。

+0

你提出了一些好點,它肯定給了我一些想法。我遇到的問題是,我處理的情況並不適用於封裝。我擔心我無法提供更多信息 - 這是工作和專有的,我實際上對我給出的數額有些擔憂。不過,我感謝你的見解,並且我會仔細觀察我的結構。 – 2009-12-15 17:22:42

+1

其實現在我已經回顧了自己的結構,看起來我的問題實際上是由星期一 - 下午睡眠剝奪腦失敗和咖啡因不足造成的。事實證明,我想隱藏上層課程的方法是爲了讓他們看到,以防他們需要覆蓋它。我不知道爲什麼我在想別的。 無論如何,要記住未來設計的經驗法則。如果有人認爲需要隱藏 - 一個人的設計是錯誤的。看封裝。感謝您的周到和詳細的答案。 – 2009-12-15 18:05:19

10

號我不知道爲什麼你會引用規範,然後問是否有沒有辦法做的規範說的正好相反......

也許如果你解釋爲什麼希望要做到這一點,你可以得到如何一些建議。

+0

好的,隱藏是解釋這個錯誤的方法。雖然這將完成我需要的結果。一個更好的方式來解釋這是從另一個方向:我需要公開一些私有基類的功能,直接從它派生的類 - 但不是上面的類。 – 2009-12-14 18:39:58

6

當覆蓋一個方法時,你只能使它更多public,而不是更私密。我不知道爲什麼你用的是「一般」

記住,訂購從最低到最高限制:

public<protected<default<private 

是,「protected」比default較少限制的訪問修飾符(無時使用修飾符),因此您可以覆蓋標記爲覆蓋方法的默認方法爲protected,但不能做相反的操作。

能: 您可以覆蓋一個protected方法與public之一。

不能: 不能覆蓋一個public方法與protected之一。

+0

你可以用'protected'在另一個包中覆蓋'protected',這很奇怪。啊,'protected'是「怪異的」。 – 2009-12-14 17:23:46

+0

當然,您始終可以使用相同的訪問修飾符來覆蓋。愛爾康詢問如何改變訪問級別。 – andandandand 2009-12-14 17:25:34

+0

固定詞「general」;)當我的頭在編碼時,不是我的力量。我知道這個規則 - 我問是否有辦法解決這個問題。 – 2009-12-14 18:36:23

6

如果你這樣做,然後DerivedOne不會是一個基地,從DerivedTwo的觀點。相反,你想要的是一個包裝類

//Uses myMethod but keeps it hidden 
public class HiddenBase { 
    private final Base base = new Base(); 
    private void myMethod(); 
    public void otherMethod() {base.otherMethod();} 
} 

不能訪問基雖然這樣的保護方法......

+0

只有私人最終基地=新基地();'。 – 2009-12-14 17:24:29

+0

啊,謝謝,我會解決這個問題。 – KernelJ 2009-12-14 17:25:41

2

你描述的靠近該protected接入類是什麼,派生班級可以訪問,其他人則無法訪問。

如果從基類繼承你有過這種沒有控制可能會帶來一個問題,你可以同時調用超直接,類似使提供給你的類繼承的代碼拋出一個異常使該方法inaccesible給他人:

// Uses myMethod and then hides it. 
public class DerivedOne extends Base { 
    @Override 
    public void myMethod() { 
     throw new IllegalStateException("Illegal access to myMethod"); 
    } 

    private void myPrivateMethod() { 
     super.myMethod(); 
    } 

} 

編輯:回答你的闡述,如果我理解正確的話,你需要在它在中產階層定義的基類的情況下指定行爲。對於來自中產階級的類,抽象的保護方法是不可見的。

一種可能的方法是使用基類中需要抽象的方法定義接口,在基類中保留私有的最終引用並在構造中間類對象時提供對實現的引用。

接口將在嵌套中產階級內部的(靜態?)來實現。我的意思是這樣的:

public interface Specific { 
    public void doSomething(); 
} 

public class Base { 
    private final Specific specificImpl; 

    protected Base(Specific s) { 
     specificImpl = s; 
    } 

    public void doAlot() { 

     // ... 

     specificImpl.doSomething(); 

     // ... 
    } 
} 

public class Middle extends Base { 

    public Middle() { 
     super(new Impl()); 
    } 

    private Impl implements Specific { 

     public void doSomething() { 

      System.out.println("something done"); 
     } 
    } 
} 

public class Derived extends Middle { 

    // Access to doAlot() 
    // No access to doSomething() 
} 
+0

嗯......這是醜陋的。是的,保護接近,但不是那裏。好主意,但。去只是不可見。如果我必須解決受保護的指導人們忽略它們,那也沒關係。有點希望有一種方法可以有限度地揭示功能,只是爲了清潔的緣故。 – 2009-12-14 18:44:27

3

繼承的工作,因爲到處都可以使用的基類,你也可以用它的一個子類。行爲可能不同,但API不是。這個概念被稱爲the Liskov substitution principle

如果您能夠限制對方法的訪問,那麼生成的類將不會具有相同的API,並且您將無法使用基類的實例替代其中一個派生類,從而否定繼承的優點。

你真的想做到能與接口做些什麼:

interface IBase1 { 
} 

class Derived1 implements IBase1 { 
    public void myMethod() { 
    } 
} 

class Derived2 implements IBase1 { 
} 

class UseMe { 
    public void foo(IBase1 something) { 
    // Can take both Derived1 and Derived2 
    // Can not call something.myMethod() 
    } 
    public void foo(Derived1 something) { 
    something.myMethod(); 
    } 
    public void foo(Derived2 something) { 
    // not something.myMethod() 
    } 
} 
+0

接口不會執行此操作,因爲存在所有寫入基類的功能,而不僅僅是一個接口。請參閱編輯以更好地解釋我實際嘗試做的事情。 – 2009-12-14 18:42:33

2

這是可能的,但需要一些包裝操作的,並可能導致是更復雜一點比你想的結構與長期合作。

考慮以下幾點:


package a; 

public class Base { 
    void myMethod() { 
     System.out.println("a"); 
    } 
} 

package a; 

public class DerivedOne extends Base { 
    @Override 
    void myMethod() { 
     System.out.println("b"); 
    } 
} 

package b; 

public class DerivedTwo extends a.DerivedOne { 
    public static void main(String... args) { 
     myMethod(); // this does not compile... 
    } 
} 

我建議是對自己好一點,你的同事和其他任何人說結束不得不維護你的代碼;重新考慮你的類和接口以避免這種情況。

1

你必須做出最後的方法時,將其覆蓋

public class Base { 
protected void myMethod() {} 
} 

// Uses myMethod and then hides it. 
public class DerivedOne extends Base { 
@Override 
final protected void myMethod(); //make the method final 
} 


public class DerivedTwo extends DerivedOne { 
    // can't access myMethod here. 
} 
相關問題