Ограничительные ключи в типовых безопасных гетерогенных контейнерах
Я хотел бы использовать шаблон безопасного контейнера общего типа, описанный в Joshua Bloch 's Эффективная Java, но хотела бы ограничить классы, которые могут использоваться в качестве ключей, используя enum. Ниже приведен код от Иисуса НавинаКнига
public class Favorites {
private Map favorites = new HashMap();
public void putFavorite(Class type, T instance) {
if (type == null)
throw new NullPointerException("Type is null");
favorites.put(type, instance);
}
public T getFavorite(Class type) {
return type.cast(favorites.get(type));
}
}
Я хотел бы написать подобный класс, но ограничить ключи, чтобы сказать "Dog.class», а также "Cat.class», В идеале приемлемые ключи должны быть описаны перечислением, а "RestrictedFavorites» класс будет принимать членов перечисления в качестве ключей. Я'Я не уверен, смогу ли я заставить компилятор сделать все это за меня (безопасность типов, ограничение по enum, общность), но если у кого-то есть предложение, ям все уши. Ниже приведена попытка 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 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 favorites = new HashMap();
//Ideally would use compile time checking
public 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 getFavorite(Class 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) {}
}
}