2012-11-19 16 views
1

(也許是爲了"How to create a generic singleton class in java?" :)爲每個類型參數爲泛型類創建單獨單例的最佳習慣用法?

class MyClass<T> { 
    private static Map<Class<MyClass<?>>, MyClass<?>> s_instances = 
     new HashMap<Class<MyClass<?>>, MyClass<?>>(); 

    public static MyClass<?> blah(Class<MyClass<?>> clz) 
      throws InstantiationException, IllegalAccessException { 
     if (s_instances.get(clz) != null) 
      return s_instances.get(clz); 
     MyClass<?> instance = clz.newInstance(); 
     s_instances.put(clz, instance); 
     return instance; 
    } 
} 

互補的問題是否有一種具有單每類型參數值更好的成語?

編輯:請不要僅僅指出缺乏線程安全性。採取的點。我在問我是否可以做比這張地圖更優雅的東西。

+0

在你的例子中,不太確定每種類型有不同的實例。新實例必須是「MyClass」(不是子類)的確切類型,所以你可以調用MyClass的構造函數(不需要反射)。 – artbristol

回答

1

請不要這樣做。
A.你的單例不是線程安全的。
B.請注意double check pattern issues at Java
C.難道真的很難在每個類的靜態初始化,並具有:

static { 
    instance = new MySingleton(); 
} 

然後

public static MySingleton getInstance() { 
return instance 
} 

如果你真的堅持 -
1.你或許可以定義一個單身人員將管理地圖中的類型到一個實例(關鍵是類或完整的類名,值是對象)
2.你可以添加註冊你想要的類型(我建議他們有私人CTORs)。
3.使用this答案以調用私人CTOR,並創建一個實例以放入地圖項的值中。
4.提供一個getInstance方法在1中提到的repoistory,具有的簽名:

public Object getInstanceByType(Class<?> clazz) 

此方法將來自內部地圖獲取實例。

+0

關於線程安全的要點(儘管對於我的具體用法來說這不是問題)。爲什麼你會推薦我不這樣做? – einpoklum

+0

因爲這意味着要向編碼器添加幾乎沒有任何實際價值的基礎設施。你可以爲每個類使用6行代碼的單例 - 這是否值得添加一個基礎結構? –

+0

我不遵循...我不知道類型參數的值是什麼,所以我不能添加這6行代碼。 – einpoklum

1

你的方法不是線程安全的:

private static Map<Class<MyClass<?>>, MyClass<?>> s_instances = 
    new HashMap<Class<MyClass<?>>, MyClass<?>>(); 

public static MyClass<?> blah(Class<MyClass<?>> clz) 
     throws InstantiationException, IllegalAccessException { 
    if (s_instances.get(clz) != null) 
     return s_instances.get(clz); 
    // here1 
    MyClass<?> instance = clz.newInstance(); 
    s_instances.put(clz, instance); 
    // here2 
    return instance; 
} 

一旦一個線程獲得過去行標//here1,之前第一個線程在標//here2行,因此創造了第二個第二個線程可能進入方法同一種「單身人士」並覆蓋地圖中的第一個。

的快速解決將是在地圖上進行同步:

public static MyClass<?> blah(Class<MyClass<?>> clz) 
     throws InstantiationException, IllegalAccessException { 
    synchronized(s_instances){ 
    if (s_instances.get(clz) != null) 
     return s_instances.get(clz); 
    // here1 
    MyClass<?> instance = clz.newInstance(); 
    s_instances.put(clz, instance); 
    // here2 
    return instance; 
    } 
} 

然而,這將意味着許多線程必須等待了很多時間,最終可能殺死你的應用程序。也許你應該做的是兩個步驟的解決方案:

public static MyClass<?> blah(Class<MyClass<?>> clz) 
     throws InstantiationException, IllegalAccessException { 
    Object candidate = s_instances.get(clz); 
    if(clz.isInstance(candidate)){ // implicit null check 
     return clz.cast(candidate); 
    } 
    synchronized(s_instances){ 
    Object candidate = s_instances.get(clz); 
    if(clz.isInstance(candidate)){ // gotta check a second time in a 
     return clz.cast(candidate); // synchronized context 
    } 
    MyClass<?> instance = clz.newInstance(); 
    s_instances.put(clz, instance); 
    return instance; 
    } 
} 

而且,一個HashMap不適合併發訪問,所以您應該在Collections.synchronizedMap()把它包:

private static Map<Class<MyClass<?>>, MyClass<?>> s_instances = 
    Collections.synchronizedMap(new HashMap<Class<MyClass<?>>, MyClass<?>>()); 

或與去改爲ConcurrentHashMap

+0

這就是爲什麼我建議在我的答案中預先註冊類型。他總是可以嘗試通過使用預先定義的地圖來減少鎖定時間,比如將N個條目指定給內部地圖。它不完全像ConcurrentMap,因爲他將管理「細分」並在應用程序啓動時創建它們,因此您不需要鎖定添加新細分。 –

相關問題