2014-01-28 30 views
2

我想使用Joshua Bloch的Effective Java中描述的泛型類型安全容器模式,但想限制通過使用枚舉可以用作鍵的類。以下是Joshua書中的代碼。類型安全異構容器中的限制鍵

public class Favorites { 

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

    public <T> void putFavorite(Class<T> type, T instance) { 
    if (type == null) 
     throw new NullPointerException("Type is null"); 
    favorites.put(type, instance); 
    } 

    public <T> T getFavorite(Class<T> type) { 
    return type.cast(favorites.get(type)); 
    } 

} 

我想編寫一個類似的類,但限制鍵說「Dog.class」和「Cat.class」。理想情況下,可接受的鍵將由枚舉來描述,而「RestrictedFavorites」類將把枚舉的成員作爲鍵。我不確定是否可以讓編譯器爲我完成所有這些事情(類型安全,枚舉限制,一般性),但如果有人有一個建議,我全都聽。以下是嘗試V1,它使用運行時檢查而不是編譯時間檢查,並不完全令人滿意。

import java.util.HashMap; 
import java.util.HashSet; 
import java.util.Map; 
import java.util.Set; 
/** 
* Attempt V1 At a "RestrictedFavorites" class 
*/ 
public class RestrictedFavorites { 

    public static enum RestrictedKey { 

    STRING(String.class), 
    INTEGER(Integer.class); 

    private static Set<Class<?>> classes; 
    static { 
     classes = new HashSet<>(); 
     for (RestrictedKey key: values()) { 
     classes.add(key.getKlass()); 
     } 
    } 

    private final Class<?> klass; 

    RestrictedKey(Class<?> klass) { 
     this.klass = klass; 
    } 

    public Class<?> getKlass() { 
     return klass; 
    } 

    public static boolean isValidClassKey(Class<?> klass) { 
     return classes.contains(klass); 
    } 

    } 

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

    //Ideally would use compile time checking 
    public <T> void putFavorite(RestrictedKey key, T instance) { 
    if (key == null) throw new NullPointerException("Type is null"); 
    if (!key.getKlass().equals(instance.getClass())) { 
     throw new IllegalArgumentException(
      "The type of the key must match the type of the instance"); 
    } 
    favorites.put(key.getKlass(), instance); 
    } 

    //Ideally would take a RestrictedKey as an argument 
    public <T> T getFavorite(Class<T> key) { 
    if (!RestrictedKey.isValidClassKey(key)) { 
     throw new IllegalArgumentException(
      "The key must be a member of RestrictedKeys"); 
    } 
    return key.cast(favorites.get(key)); 
    } 

} 

下面是一些單元測試來驗證我的課是做什麼大概我也想:

public class RestrictedFavoritesTest extends TestCase { 

    public void testPutFavorite() { 
    RestrictedFavorites myFavorites = new RestrictedFavorites(); 
    myFavorites.putFavorite(RestrictedKey.INTEGER, 1); 
    myFavorites.putFavorite(RestrictedKey.STRING, "hey"); 
    int expectedInt = myFavorites.getFavorite(Integer.class); 
    assertEquals(1, expectedInt); 
    String expectedString = myFavorites.getFavorite(String.class); 
    assertEquals("hey", expectedString); 
    } 

    public void testPutFavorite_wrongType() { 
    RestrictedFavorites myFavorites = new RestrictedFavorites(); 
    try { 
     myFavorites.putFavorite(RestrictedKey.INTEGER, "hey"); 
     fail(); 
    } catch (IllegalArgumentException expected) {} 
    } 

    public void testPutFavorite_wrongClass() { 
    RestrictedFavorites myFavorites = new RestrictedFavorites(); 
    try { 
     myFavorites.getFavorite(Boolean.class); 
    } catch (IllegalArgumentException expected) {} 
    } 

} 
+2

使用'enum'作爲'Map'鍵和存儲在'enum'的屬性'Class'類型。 –

+0

我試過這種方法。我已經爲我的問題添加了一個示例,但它不能編譯。如果我能弄清楚什麼,我會迭代併發布更好的版本。 –

+0

在你的'getFavorite'中將返回類型轉換爲'T'。如果你想限制你的'Map'可以容納的實例沒有一些共同的父類,你將不得不依賴運行時檢查。你可以把這個方法放到你的'enum'中 - 它會稍微清理一下代碼。 –

回答

1

答案(我自己的問題)。不要使用枚舉。因爲枚舉不能是通用的。相反,創建一個類來表示受限制的鍵,並限制對構造函數的訪問。枚舉有效的鍵作爲字段。

import java.util.HashMap; 
import java.util.Map; 

public class RestrictedFavorites { 

    private static final class RestrictedKey<T> { 

    private final Class<T> type; 

    private RestrictedKey(Class<T> type) { 
     this.type = type; 
    } 

    private Class<T> getMyType() { 
     return this.type; 
    } 
    } 

    public static final RestrictedKey<String> STRING_KEY = 
     new RestrictedKey<>(String.class); 
    public static final RestrictedKey<Integer> INTEGER_KEY = 
     new RestrictedKey<>(Integer.class); 

    private final Map<RestrictedKey<?>, Object> favorites = 
     new HashMap<RestrictedKey<?>, Object>(); 

    public <T> void putFavorite(RestrictedKey<T> key, T instance) { 
    favorites.put(key, instance); 
    } 

    public <T> T getFavorite(RestrictedKey<T> key) { 
    return key.getMyType().cast(favorites.get(key)); 
    } 

} 

而且單元測試:

public class RestrictedFavoritesTest extends TestCase { 

    public void testPutFavorite() { 
    RestrictedFavorites myFavorites = new RestrictedFavorites(); 
    myFavorites.putFavorite(RestrictedFavorites.STRING_KEY, "hey"); 
    myFavorites.putFavorite(RestrictedFavorites.INTEGER_KEY, 1); 
    assertEquals(new Integer(1), myFavorites.getFavorite(RestrictedFavorites.INTEGER_KEY)); 
    assertEquals("hey", myFavorites.getFavorite(RestrictedFavorites.STRING_KEY)); 
    } 

} 
+1

+1,不要忘記也使領域'最後'。 –

+0

完成。感謝您的建議。 –

+0

這與從簡單指定一個靜態(或可配置)的允許類'類的集合有什麼不同?你只是將一個包裝器添加到'Class'對象中。我不明白。 –