【发布时间】:2014-01-29 06:12:57
【问题描述】:
我想使用 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) {}
}
}
【问题讨论】:
-
使用
enum作为Map中的键,并将Class类型存储在enum的属性中。 -
我试过这种方法。我在我的问题中添加了一个示例,但它没有编译。如果我能解决问题,我会迭代并发布一个更好的版本。
-
在您的
getFavorite中将返回类型转换为T。如果您想限制Map在没有一些公共父类的情况下可以保存的实例,您将不得不依赖运行时检查。您也可以将该方法放入您的enum中 - 它会稍微整理代码。 -
是否可以选择让最喜欢的类实现标记接口?