2010-06-24 83 views
26

假設我有這些接口:實現多個接口預仿製藥使用參數

public interface I1 { 
    void foo(); 
} 

public interface I2 { 
    void bar(); 
} 

和類:

public class A extends AParent implements I1, I2 { 
    // code for foo and bar methods here 
} 

public class B extends BParent implements I1, I2 { 
    // code for foo and bar methods here 
} 

public class C extends CParent implements I1 { 
    // code for foo method here 
} 

現在,隨着仿製藥我能有這樣的方法:

public <T extends I1 & I2> void method(T param) { 
    param.foo(); 
    param.bar(); 
} 

我可以用A和B作爲參數來調用它,但不能用C(它不實現I2)來調用它。

有沒有辦法實現這種類型的安全預泛型(java < 1.5)。

考慮到A,B和C具有不同的繼承樹,並且它不是一個真正的選項,可以像AParent和BParent一樣擁有一個共同的父項。

我知道你可以這樣做:

public void method(I1 param) { 
    param.foo(); 
    ((I2)param).bar(); 
} 

但你也可以撥打method(new C())不落實I2,讓你陷入困境。

那麼有沒有其他方法可以做到這一點?

P.S. :我不需要這麼做,我主要是出於好奇而問。

回答

20

創建第三個接口I3擴展了I1和I2。然後,A類和B類都實現I3,並且通用方法接受I3。

這也許是唯一的方法來做到這一點。

+4

我有點意識到這一點後,我問的問題,但如果你有兩個以上的接口,並希望混合和匹配那些不同的方法,它有可能成爲爲每個你想要使用的組合創建一個子界面。我正在考慮更多地採用像'method(I1&I2 param)'這樣的方法或者其他方式來說明參數在旅途中實現多個接口(不創建新的類/接口) – 2010-06-24 08:22:25

+0

我不認爲這從設計的角度來看很好。請看我的答案。 – hqt 2016-04-30 13:32:56

2

在Java 1.5之前,IMO沒有辦法在編譯時實現這種類型安全。但是在運行時使用「instanceof」有一個靈魂。

public void method(Object o) { 
    if (!(o instanceof I1)) 
    throw new RuntimeException("o is not instance of I1"); 

    if (!(o instanceof I2)) 
    throw new RuntimeException("o is not instance of I2"); 

    // go ahead ... 
} 
+0

那麼如果你在問題中調用'method(new C())'作爲我的非泛型例子,那麼在調用'I2'來調用'bar()'時會得到'ClassCastException',所以沒有太大的改進這裏。 – 2010-06-24 08:27:30

+0

不,你會得到一個RuntimeException之前,因爲「(o instanceof I2)」的檢查結果爲false;) 嗯,我猜sri的建議可能更好:)因爲你不會得到運行時異常與我的建議一樣,因爲編譯器會在以前失敗。 – VuuRWerK 2010-06-24 08:45:24

2

sri是,如果你有權限更改A和B的簽名然而最好的答案,如果你沒有權限,那麼你可以做:

public void method(I1 param1 , I2 param2) { // unpopular classes that do not implement I3 must use this method 
    param1.foo(); 
    param2.bar(); 
} 
public void method(I3 param){ // I hope everybody implements I3 when appropriate 
    param.foo(); 
    param.bar(); 
} 
public void method(A param){// A is a popular class 
    method(param,param); 
} 
public void method(B param){// B is a popular class 
    method(param,param); 
} 

當然,現在只需使用泛型。

+0

是的,這是行得通的,但是如果I1類似於Comparable,I2類似於被廣泛使用的Serializable類型,那麼爲每個類型實現兩者(稱爲私有類型)的方法可能有點難度。當我想到這個問題時,我只是以A和B爲例,在調用方法時我並不在乎它們是什麼(唯一重要的是它們實現了這些接口)。事實上,如果'method()'可能是一個暴露給其他人使用的API方法,那麼可以像這樣使用它的類進行硬編碼將是一個問題。 – 2010-06-24 11:22:02

+0

然後一個更好的選擇是公開私有方法,然後爲最感興趣的類提供一些便利方法 - 其中可能包括sri的I3接口。 – emory 2010-06-24 12:10:37

6

我不認爲上面的答案在設計上是好的。當你創建一個接口時,你需要確保調用者對象具有責任對於在該接口中定義的一些動作。所以有兩種解決方案在上面討論,我會說出爲什麼這些解決方案在設計觀點上不好。

1.化妝一個接口同時擴展兩個接口:

public interface IC extends IA,IB { 
    // empty method here 
} 

上面的代碼是無感。你可以定義一個單獨的接口來組合其他接口,並且裏面沒有任何新的方法。您對IC沒有任何價值,而不是將兩個接口IAIB組合在一起。而在某些情況下,這種方式會使你的代碼在你找不到第三個界面的合適名稱時「很有趣」。這種情況會導致內部法鑄造如IReadableAndWriteableISomethingAndSomethingAndAnotherThing

2.鍵入一些接口名稱:

public void methodA(IA object) { 
    if (object instance of IB) { 
    ((IB)(object)).someMethod() 
} 

這種方式是無稽之談了。爲什麼輸入參數是IA,那麼你必須從界面IB執行一些操作?在程序員的觀點下,除了閱讀文檔之外,沒有辦法知道這一點。這對設計一個供其他人使用的功能並不好。

真正的解決方案:

上述解決方案存在設計上的一個問題:程序員使用具有兩個接口負責一個對象。如果程序員不想這樣做,會出現什麼問題。他們希望爲每個不同的接口使用兩個不同的具體類,以便於測試,乾淨的代碼? 他們不能。

你應該在你的方法簽名兩個不同的參數:

public void exampleMethod(IA object1, IB object2) { 
    object1.someMethod() 
    object2.someMethod() 
} 

並調用上面的方法,你把相同的參數內(如果程序員使用同一個對象)。他們也可以放置不同的物體。

public void caller() { 
    IC object3 = new C(); // C is the class implements both IA and IB 
    exampleMethod(object3, object3); 
    IA objectA = new A(); 
    IB objectB = new B(); 
    exampleMethod(objectA, objectB);  
} 

希望這有助於:)

+2

對不起,我醒了一個老話題,但我認爲增加是值得的。我真的認爲這個答案是最好的。然而,對於第一點來說,是否有意義是相當主觀的(至少它可以作爲一種臨時解決方案),但是有一個真正客觀實際的例子表明它不是一個好設計:if你實現了一個擴展了相關接口的類,但不包括將它們組合在一起的接口,那麼你不能在要求組合接口的方法中使用它,儘管它遵守了初始合同。 – Matthieu 2017-03-21 07:51:33

+1

我無法編輯以前的答案,因此我添加了另一個答案:製作組合界面的想法要麼不好,要麼不完整。它不應該只是一個接口,而應該是一個包裝:如果你從外部庫獲得你的實現,但希望兩個接口的任何實現都可用,所以你不想被lib所限制,那麼你需要一個包裝,或一個適配器,如果你喜歡。這個解決方案確保你實際上有兩個接口關聯到同一個實例,而上面的hqt後面不允許確保這樣的約束,這仍然是值得的。 – Matthieu 2017-03-21 08:17:09