2011-11-10 21 views
4

我不認爲問題的標題會很明確,但這個想法很簡單。我如何在Java Map接口中將泛型類型參數的值關聯起來?

假設我有一個Map類型變量。

Map<K,V> myMap; 

但我想建立例如K和V之間的關係,我想說 這個地圖涉及的一些類的設置該類的OBJETS。喜歡的東西:

Map<Set<T>, T> myMap; 

而不是針對特定類型的T.我想這個地圖接受像

(Set<String>, String), 
(Set<Integer>, Integer) 
... 

項是否有MYMAP可能的聲明,讓我有這種行爲?如果我錯誤地解釋自己,或者如果我有以前的概念性錯誤,請告訴我。

+0

你期待的是把後一個空'設置'在跟隨一個空'設置' ,一個空的'Set '將被取消映射或返回一個'String'(/拋出'ClassCastException')? –

回答

0

仿製藥沒有辦法讓編譯器爲每個put()調用驗證不同的T。換句話說,你不能相同地圖和做的事:

myMap.put(new HashSet<String>(), "foo"); 
myMap.put(new HashSet<Integer>(), 1); 

如果你需要這個,那麼你可能要存儲<Object>,做自己使用instanceof或一些其他黑客的驗證。現在

,你絕對可以做這樣的事情:

public class MyMap<T> extends HashMap<Set<T>, T> { 
    ... 

然後,你可以這樣做:

MyMap<String> myMap = new MyMap<String>(); 
Set<String> set = new HashSet<String>(); 
myMap.put(set, "foo"); 

記住,關鍵必須有一個有效的hashCode()equals()方法,這可能是昂貴的一個Set

+0

不錯!我沒有想到創建一個命名類來解決這個問題。不要擔心性能問題,套件只是一個例子,我的用例不同。 –

+0

這仍然會強制您放置相同類型的鍵值對。據我所知,OP希望能夠將不同類型的鍵值對放置在單個映射中,其中該值與鍵的集合具有相同的類型。這種類型在班級上沒有定義,而是在個別方法上。 – BalusC

+1

據我瞭解他想要的地圖可以包含字符串和整數條目,但映射需要匹配。因此,將整數設置爲整​​數,將字符串設置爲字符串,但相同的地圖可以包含這兩種類型。 – Stefan

1

你試圖做的似乎不是一個好diea,因爲即使是相同的類型,每個Set<T>總是不等於另一個Set<T> - 使用Sets作爲關鍵字或多或少是無用的。

這就是說,你並不需要定義一個新的類 - 你可能需要一種方法來接受這樣的地圖:

public static <T> void process(Map<Set<T>, T> map) { 
    for (Map.Entry<Set<T>, T> entry : map) { 
     Set<T> key = entry.getKey(); 
     T value = entry.getValue(); 
     // do something 
    } 
} 
0

我不認爲這是可能實現的編譯時間檢查Java泛型。然而,它在運行時非常簡單。恰到好處短裝飾:

public class FancyTypeMapDecorator implements Map<Set<? extends Object>, Object> { 

    final Map<Set<? extends Object>, Object> target; 

    public FancyTypeMapDecorator(Map<Set<? extends Object>, Object> target) { 
     this.target = target; 
    } 

    @Override 
    public Object put(Set<? extends Object> key, Object value) { 
     final Class<?> keyElementType = key.iterator().next().getClass(); 
     final Class<?> valueType = value.getClass(); 
     if (keyElementType != valueType) { 
      throw new IllegalArgumentException(
       "Key element type " + keyElementType + " does not match " + valueType); 
     } 
     return target.put(key, value); 
    } 

    @Override 
    public void putAll(Map<? extends Set<? extends Object>, ? extends Object> m) { 
     for (Entry<? extends Set<? extends Object>, ? extends Object> entry : m.entrySet()) { 
      put(entry.getKey(), entry.getValue()); 
     } 
    } 

    //remaining methods are simply delegating to target 

} 

下面是它如何工作的:

final Map<Set<? extends Object>, Object> map = 
    new FancyTypeMapDecorator(new HashMap<Set<? extends Object>, Object>()); 

Set<? extends Object> keyA = Collections.singleton(7); 
map.put(keyA, 42); 

Set<? extends Object> keyB = Collections.singleton("bogus"); 
map.put(keyB, 43); 

put拋出異常。

然而,這兩個實現(我甚至不意味着它會失敗的空Set作爲一個關鍵)和使用/ API觸發警鐘......你真的想要處理這樣的結構?也許你需要重新考慮你的問題?你究竟是試圖實現?

+0

我不認爲你想匹配確切的運行時實現類型。 –

+0

你說得對,OP總是可以使用'valueType.isAssignableFrom(keyElementType)'或其他方式 - 取決於需求。 –

2

不幸的是,這對於Java泛型是不可行的。如果你的Java允許高階類型的參數,那麼可以定義Map類似:

public interface Map<V<>> { // here V<> is my hypothetical syntax for a 
          // type parameter which is itself generic... 
    <K> 
    V<K> put(K key, V<K> value); 
    ... 
} 

而不是實際的java.util.Map

public interface Map<K, V> { 
    V put(K key, V value); 
    ... 
} 

你可以看到,問題是,K聲明一次爲整個班級,而不是每次致電.put()

足夠的幻想,所以你能做什麼?我認爲最好的是創建一個Map<Set<?>, Object>並將其作爲私人成員包裝。然後,你可以自由地創建自己的put()get()其考慮類型之間的預期「關係」:

class SpecialMap { 
    private Map<Set<?>, Object> map = ...; 

    public <T> 
    T put(Set<T> key, T value) { 
     return (T) map.put(key, value); 
    } 

    public <T> 
    T get(Set<T> key) { 
     return (T) map.get(key); 
    } 
}