2017-10-18 165 views
2

正如標題所示,我試圖設計一個自定義數據結構SearchTree,對於SearchTree.Entry<K, V>等條目,該數據結構需要爲Iterable。 A SearchTree本身只是一個界面。接口及其子類及其類型和子類型的泛型迭代器

我想要做的是有一個與它自己的KV亞型實現SearchTree<K, V>任何類也能夠實現迭代器作爲一個SearchTree<K, V>接口中定義。

SearchTree.java

public interface SearchTree<K extends Comparable<? super K>, V> extends Iterable<SearchTree.Entry<K, V>> { 

    static interface Entry<K, V> { 

     K getKey(); 

     V getValue(); 

     void setValue(V value); 
    } 
} 

現在假設我有一個實現該接口的類。

BST.java

public class BST<K extends Comparable<? super K>, V> implements SearchTree<K, V> { 

    @Override 
    public Iterator<Entry<K, V>> iterator() { 
     // return some bst specific iterator 
    } 
} 

BSTNode.java

public class BSTNode<K extends Comparable<? super K>, V> implements SearchTree.Entry<K, V> { // ... 
} 

現在,很明顯,BST Iterator應該遍歷BSTNode對象,因此它將使意義它申報的東西如:

BSTIterator.java

public class BSTIterator<K extends Comparable<? super K>, V> implements Iterator<BSTNode<K, V>> { 
} 

但現在回從BST.java問題,其中的BSTIterator實例應返回,就像這樣:

BST.java

public class BST<K extends Comparable<? super K>, V> implements SearchTree<K, V> { 

    @Override 
    public Iterator<Entry<K, V>> iterator() { 
     return new BSTIterator<>(); 
    } 
} 

現在這是行不通的:無法推斷BSTIterator <>的類型參數。 是否有任何明智的方法來解決此問題,以便我可以在我的接口中使用泛型迭代器,並且以類似方式實現SearchTree的類的返回混凝土迭代器實現,以使泛型類型也可以被子類化?

+0

放下'?來自「可比較的」的超級。將一個對象與它的超類型的一個實例進行比較是沒有任何意義的。 –

回答

2

你很近。您的迭代器BSTIterator必須實現Iterator<SearchTree.Entry<K, V>>而不是Iterator<BSTNode<K, V>>。我認爲這是因爲您返回的迭代器的類型必須保證它是任何Entry<K, V>,而不是BSTNode

public class BSTIterator<K extends Comparable<? super K>, V> 
    implements Iterator<SearchTree.Entry<K, V>> { 
    // ... 
} 

public class BST<K extends Comparable<? super K>, V> 
    implements SearchTree<K, V> { 

    @Override 
    public Iterator<SearchTree.Entry<K, V>> iterator() { 
     return new BSTIterator<>(); 
    } 
} 
0

有幾種方法可以解決這個問題。


最直接的方法是什麼Neil describes in his answer。但是這會丟棄類型信息:例如,如果您想直接使用BSTIterator,則需要將元素轉換回BSTNode,如果您想要這樣使用它們。


的哈克(但實際上是完全安全)的方法是注意有上Iterator<T>沒有消費者的方法:你永遠只能得到通過get()從它的值(或者檢查是否有其他元素或刪除以前得到的元素)。

因此,使用Iterator<SubclassOfT>作爲Iterator<T>是完全安全的。 Java的類型系統不知道它是安全的,所以你要投:

return (Iterator<SearchTree.Entry<K, V>>) (Iterator<?>) new BSTIterator<K, V>(); 

,並添加@SuppressWarnings("unchecked")


毛-IN-A-不同路的方式來做到這一點是讓SearchTree實現可迭代通過一個上限類型:

public interface SearchTree<K extends Comparable<K>, V> 
    extends Iterable<? extends SearchTree.Entry<K, V>> 

這是一個有點噁心,因爲現在iterator()方法將返回一個Iterator<? extends SearchTree.Entry<K, V>>:你被困在那個?

它由Josh Bloch在有效Java第二版聲稱,如果您的API的用戶必須關心通配符,您設計它是錯誤的。 (第28項,特別是第137頁,如果你有一份副本)。


這樣做是爲了在Iterator明確地添加一個類型變量的元素類型的另一種方法:

public interface SearchTree<K extends Comparable<K>, V, T extends SearchTree.Entry<K, V>> 
    extends Iterable<T> 

現在,你可以宣佈你的BST爲:

public class BST<K extends Comparable<K>, V> 
    implements SearchTree<K, V, BSTNode<K, V>> 

這表達了最多的類型信息,但我不會聲稱這絕對更好,因爲每當你聲明一個變量持有一個時就有3個類型變量只是通用的雜波,尤其是第三個通常會包含第一個和第二個,例如,

SearchTree<String, Integer, ? extends SearchTree.Entry<String, Integer>> 

有沒有超級大清潔選項在這裏。就我個人而言,我會選擇第一個hacky選項,因爲a)它確實很安全! b)黑客隱藏在實施課堂內部。