簡體   English   中英

如何在java Map接口中關聯泛型類型參數的值?

[英]how can I relate values of generic type parameters in java Map interface?

我不認為問題的標題會很清楚,但這個想法很簡單。

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

Map<K,V> myMap;

但是我想建立一個K和V之間的關系,例如,我想說這個Map將某些類的集合與該類的objets相關聯。 就像是:

Map<Set<T>, T> myMap;

但不是特定類型T.我希望這個地圖接受像這樣的條目

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

是否有可能的myMap聲明允許我有這種行為? 如果我錯誤地解釋自己或者我之前有過概念錯誤,請告訴我。

遺憾的是,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);
    }
}

你想要做的事情似乎不是一個好的迪亞,因為每個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
    }
}

對於每個put()調用,編譯器無法使用泛型驗證不同的T 換句話說,你不能擁有相同的地圖並做:

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

如果你需要這個,那么你可能必須存儲<Object>並使用instanceof或其他一些hack自己進行驗證。

現在,你絕對可以這樣做:

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可能很昂貴。

我認為用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作為鍵而失敗)和usage / API觸發警鍾......你真的想要處理這樣的結構嗎? 也許你需要重新思考你的問題? 究竟想要實現什么目標?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM