2013-06-12 25 views
5

我有對夫婦提供的接口分配一個常規類的子類的父類這個類的

public interface Finder<S extends Stack<T>,T extends Item> { 
    public S find(S s, int a); 
} 

public interface Stack<T extends Item> { 
    Stack<T> getCopy(); 
} 

和類實現了第一:

如果我不能改變任何界面什麼是最好的行動方案,同時保持實施儘可能通用?

編輯 一些其他的代碼,我不能打破實例SimpleFinder<ClassA,ClassB>所以我應該有兩個泛型類型的實現也是如此。

+0

最佳行動方案實現什麼?爲什麼你無法更改接口定義? – Bobulous

+0

嘗試蠻力施放。有辦法解決它(遞歸類型參數),但它不值得。 – ZhongYu

+0

@Arkanon,因爲它不是我的改變...:|而蠻力施展是相當醜陋的不是嗎? – SadStudent

回答

3

問題是,顯然Stack<T>不是S extends Stack<T>。 Java是強類型的,不會讓你做這樣的事情。

您可以投射到Stack<T>,在這種情況下,您仍然會收到關於未經檢查的轉化的警告。這意味着此轉換是不安全

public class SimpleFinder<S extends Stack<T>, T extends Item> implements Finder<S, T> { 

    @Override 
    public S find(S s, int a) { 
     Stack<T> stack = s.getCopy(); 
     return (S) stack; 
    } 

} 

或者乾脆用Stack<T>代替S extends Stack<T>,這是我的建議:

public class SimpleFinder<T extends Item> implements Finder<Stack<T>, T> { 

    @Override 
    public Stack<T> find(Stack<T> s, int a) { 
     Stack<T> stack = s.getCopy(); 
     return stack; 
    } 

} 
+0

謝謝!是的,我很清楚它沒有工作的原因我只是不知道如何解決這個問題,而沒有「失去」普遍性:\我不能做第二個解決方案的原因是有另一個不是我的實例化的代碼SimpleFinder 和我不能打破它... – SadStudent

+0

我可以要求兩個參數,併發送',T>'像你建議的界面,但然後'S'將是多餘的,這是一種醜陋不是嗎? – SadStudent

+2

未經檢查的強制轉換選項是不安全的 - 我建議將其從答案中刪除,或者至少添加更有意義的免責聲明。 –

0
public class SimpleFinder<S extends Stack<T>,T extends Item> implements Finder<S,T>{ 
    public S find(S s, int a){ 
     Stack<T> stack = ....; 
     ... 
     stack = s.getCopy(); 
     ..... 
     return (S) stack; 
    } 
} 

應該工作。請記住,堆棧必須是Stack<T>而不是S以匹配getCopy()返回類型。我期望S類型是好的,因爲它延伸Stack<T>,但實現它,這是我觀察到的行爲。

+0

那不能編譯... – m0skit0

+0

對不起,我剛剛編輯過,我希望現在可以。 –

+0

不能編譯:「缺少方法的返回類型」。該返回類型無效(直至Java 7)。 – m0skit0

0

你型SStack<T>一個亞型,但複製方法是上溯造型到一個Stack<T>可能的Stack<T>任何亞型。 您必須將複製結果複製到S

2

既然你不能改變的接口,你別無選擇,只能做畜生演員。

在一個更普遍的討論中,我們需要的是「自我類型」,我們想說方法調用foo.bar()應該返回靜態類型foo。通常,自我類型需要流利的API,方法應該返回foo本身。在你的情況下,你想返回一個新的對象。

在java中,自我類型沒有令人滿意的答案。一個竅門是通過自引用類型參數,如Foo<T extends Foo<T>>,但它非常難看,並且它不能強制任何子類型Bar必須是Foo<Bar>。而這個竅門根本無助於你的情況。

另一個竅門可以工作

public interface Stack<T extends Item> { 
    <X extends Stack<T>> X getCopy(); 
} 

這裏,調用者提供精確的返回類型。

 S stack = ....; 
    ... 
    stack = s.getCopy(); 
    // compiles, because X is inferred to be S 

這個技巧有助於簡化呼叫站點。然而,粗暴的強制轉換依然存在,隱藏在getCopy()的實現中。這個技巧是危險的,調用者必須知道它在做什麼。我個人不會這麼做;強制呼叫者執行演員會更好。

1

正如在評論中討論,你的設計的必要條件是getCopy方法返回「自我型」 - 也就是說,一個BlueStack<T>實施將有望從getCopy返回BlueStack<T>RedStack<T>應該返回一個RedStack<T>

不幸的是,在Java中沒有辦法表達「自我類型」。作爲zhong.j.yu points out,遞歸類型參數接近,例如:

//not recommended! 
public interface Stack<S extends Stack<S, T>, T extends Item> { 
    S getCopy(); 
} 

但作爲zhong.j.yu提到這是直觀和仍然無法阻止BlueStack<T>從「說謊」和返回來自getCopyRedStack<T>

相反,我建議重新設計。嘗試解除從Stack類型本身複製堆棧的責任。例如:

public interface StackCopier<S extends Stack<T>, T extends Item> { 

    S copy(S original); 
} 

如果StackCopier實現需要訪問他們各自的Stack S的私有成員,可以考慮讓他們嵌套類,例如:

class BlueStack<T extends Item> implements Stack<T> { 

    ... 

    static class Copier<T extends Item> implements StackCopier<BlueStack<T>, T> { 

     @Override 
     public BlueStack<T> copy(BlueStack<T> original) { 

      ... 
     } 
    } 

當然SimpleFinder會需要改變,以要麼有StackCopier<S, T>字段,要麼將其作爲find的新參數:

private final StackCopier<S, T> copier = ...; 

public S find(S stack, int a) { 

    S stackCopy = copier.copy(stack); 

    ... 

    return stackCopy; 
} 
+0

甚至認爲恕我直言需要重新設計是不幸的,正如我在問題中提到的,我不能改變接口只實現它,所以我想我堅持一個醜陋的演員,但我認爲你的解決方案是非常好的... ... – SadStudent

相關問題