2017-04-19 38 views
0

所以這裏有一個稍微棘手的問題(對我來說)。可變泛型作爲值的Java地圖

我有一個通用的對象。稱它爲MyObject。這個對象有其返回類型T的一些方法:

public class MyObject<T> 
{ 
    private T _t; 

    public MyObject(T t) 
    { 
     _t = t; 
    } 

    //... 

    public T get() 
    { 
     return _t; 
    } 
} 

(顯然,我的「MyObject來」做多一點,但是這是要點)。

現在,我想有地圖這種類型的:

Map<String, MyObject<?>> m = new HashMap<>(); 

我希望能夠獲取使用一些預定義的字符串名稱的地圖,以及這些地圖可以是任何的MyObject的。例如,我可以打電話:

m.put("map_1", new MyObject<String>("String")); 
m.put("map_2", new MyObject<Integer>(new Integer(3)); 
m.put("map_3", new MyObject<Long>(new Long(5)); 

但是 - 這裏是棘手的部分 - 我要地圖「記住」參數化類型爲MyObject,當我取回從地圖一定的價值。使用

m.get("map_1"); 

將返回

MyObject<Object> 

類型中,由於地圖的定義爲含有

MyObject<?> 

值。因此:

m.get("map_1").get() // <-- This is an Object, not a String! 

什麼修改(如果有的話)是可能的,爲了能夠得到正確的 - 全 - 關於MyObject來獲取目標信息,從而調用的最後一行(m.get(「map_1 「))將返回一個

MyObject<String> 

謝謝:)

阿米爾。

+2

沒有什麼類型安全的,你可以在這裏做。 –

+1

有一個設計實例,我認爲在Joshua Bloch的* Effective Java中可以滿足你的要求的異構類型 – markspace

+0

反射怎麼樣? – calbertts

回答

3

距離Joshua Bloch的有效的Java類型安全的異構集裝箱可能會在這裏工作。基本上你添加一個Class對象來表示類型。

public class MyObject<T> 
{ 
    private T _t; 
    private Class<T> type; 

    public MyObject(Class<T> type, T t) 
    { 
     _t = t; 
     this.type = type; 
    } 

    //... 

    public T get() 
    { 
     return _t; 
    } 

    public Class<T> getType() { return type; } 
} 

然後,你可以做這樣的事情:

public <T> T get(Map<String, MyObject<?>> map, String key, Class<T> type) { 
    return type.cast(m.get(key).get()); 
} 

是一種安全,將編譯,但如果你的類型錯誤將引發運行時錯誤。我注意到我沒有真正編譯它,所以我可能有語法錯誤,但大多數人不知道如何使用Class來投射對象。)

+0

嘿,謝謝你 - 這看起來與我所需要的非常接近,但是一個問題 - 據我所知,「類」參數就像是一個傳遞類型信息的「標記」,這樣通用方法get 會知道我們想得到一個「T」。那麼我可以簡單地傳遞一個T對象,不是嗎?它可能是公開的 T get(Map > map,String key,T t){...} – amirkr

+0

我不明白爲什麼我們會將類作爲單獨的字段存儲,當每個對象包含它的類已經。 –

+1

@amirkr我認爲當你想傳遞一個層次結構的對象時就會發生這種區別。例如,你實際上傳入了一個'JLabel',但你希望它的類型是'JComponent'。 – markspace

0

你可以上課。

Class c = m.get("map_1").get().getClass(); 
if (String.class.equals(c)) { 
    System.out.println("its a String"); 
} 

這是一個完整的測試。

public class GenericsTest { 

    /** 
    * @param args the command line arguments 
    */ 
    public static void main(String[] args) { 

     Map<String, MyObject<?>> map = new HashMap<>(); 
     MyObject<String> obj = new MyObject<>("hello"); 

     map.put("greeting", obj); 

     Class c = map.get("greeting").get().getClass(); 
     if (String.class.equals(c)) { 
      System.out.println("its a String"); 
     } 

    } 

    static class MyObject<T> { 

     T t; 

     public MyObject(T t) { 
      this.t = t; 
     } 

     T get() { 
      return t; 
     } 

    } 

} 
+0

嘿,謝謝!我不僅需要獲得該類,而且還需要* act *在它上面 - 所以如果我得到了一個字符串,代碼將「知道」它的一個字符串並且能夠對它的方法進行操作。在這裏的另一個評論(我希望)中討論了可能對這個特定上下文有幫助的響應。對於這種努力,「Class」對象機制對我來說仍然有點模糊,你的代碼幫助:) – amirkr

+0

我不認爲你需要將Class作爲單獨的字段存儲,當每個對象已經包含它的Class。 –

0

類型系統只知道類型而不是對象,因此無法區分"key1""key2",因爲它們都是String類型。

如果鍵有不同的類型,最簡單的方法是封裝弱類型的地圖,並使用反射強制類型轉換來證明編譯器的類型是正確的:

class Favorites { 

    private Map<Class<?>,?> map = new HashMap<>(); 

    <V> V get(Class<V> clazz) { 
     return clazz.cast(map.get(clazz)); 
    } 

    <V> void put(Class<V> clazz, V value) { 
     map.put(clazz, value); 
    } 
} 

Favorites favs = new Favorites(); 
favs.put(String.class, "hello"); 
favs.put(Integer.class, 42); 
favs.get(String.class).charAt(1); 
+0

嘿,謝謝。這些鍵具有相同的類型(在我的示例中 - 它們都是字符串),但值有不同的類型。我想要的是能夠在用某個鍵調用「get」方法時直接推斷類型 - 因此,我得到了一個String代碼「將知道」(並允許我使用字符串的方法)等。 Class的用法很有趣,正如另一個評論中也提到的那樣。感謝您輸入(雙關語);) – amirkr