我想创建一个地图,它将提供泛型的好处,同时支持多种不同类型的值。我认为以下是泛型集合的两个主要优点:
- 编译时警告将错误的东西放入集合中
- 从集合中取出东西时无需强制转换
所以我想要的是一张地图:
- 它支持多个值对象,
- 检查放入地图的值(最好在编译时)
- 知道从地图获取时对象值是什么。
使用泛型的基本情况是:
Map<MyKey, Object> map = new HashMap<MyKey, Object>();
// No type checking on put();
map.put(MyKey.A, "A");
map.put(MyKey.B, 10);
// Need to cast from get();
Object a = map.get(MyKey.A);
String aStr = (String) map.get(MyKey.A);
我找到了解决第二个问题的方法,方法是创建一个 AbstractKey,它由与此键关联的值类生成:
public interface AbstractKey<K> {
}
public enum StringKey implements AbstractKey<String>{
A,B;
}
public enum IntegerKey implements AbstractKey<Integer>{
C,D;
}
然后我可以创建一个 TypedMap,并覆盖 put() 和 get() 方法:
public class TypedMap extends HashMap<AbstractKey, Object> {
public <K> K put(AbstractKey<K> key, K value) {
return (K) super.put(key, value);
}
public <K> K get(AbstractKey<K> key){
return (K) super.get(key);
}
}
这允许以下操作:
TypedMap map = new TypedMap();
map.put(StringKey.A, "A");
String a = map.get(StringKey.A);
但是,如果我为键输入错误的值,我不会收到任何编译错误。相反,我ClassCastException
在 get() 上获得了运行时。
map.put(StringKey.A, 10); // why doesn't this cause a compile error?
String a = map.get(StringKey.A); // throws a ClassCastException
如果这个 .put() 可以给出编译错误,那将是理想的。作为当前的第二好,我可以ClassCastException
在 put() 方法中抛出运行时。
// adding this method to the AbstractKey interface:
public Class getValueClass();
// for example, in the StringKey enum implementation:
public Class getValueClass(){
return String.class;
}
// and override the put() method in TypedMap:
public <K> K put(AbstractKey<K> key, K value){
Object v = key.getValueClass().cast(value);
return (K) super.put(key, v);
}
现在,在ClassCastException
放入地图时会抛出 ,如下所示。这是更可取的,因为它允许更轻松/更快的调试来识别错误的键/值组合已放入 TypedMap 的位置。
map.put(StringKey.A, 10); // now throws a ClassCastException
所以,我想知道:
- 为什么不会
map.put(StringKey.A, 10)
导致编译错误? 我如何调整此设计以在放置时获得有意义的编译错误,其中值不是键的关联泛型类型?
这是一个合适的设计来实现我想要的(见顶部)?(任何其他想法/评论/警告也将不胜感激......)
是否有替代设计可以用来实现我想要的?
编辑 - 澄清:
- 如果你认为这是一个糟糕的设计——你能解释一下为什么吗?
- 我使用 String 和 Integer 作为示例值类型 - 实际上,我有许多不同的键/值类型对,我希望能够使用它们。我想在一张地图中使用它们——这就是目标。