我想创建一个映射,该映射将提供泛型的好处,同时支持多种不同类型的值。我认为以下是通用集合的两个主要优点:
所以我想要一张 map :
使用泛型的基本情况是:
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);
但是,如果我为 key 输入了错误的值,则不会出现任何编译错误。相反,我在get()上获得了一个运行时
ClassCastException
。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
放入 map 时会如下所示。这是可取的,因为它允许更容易/更快地调试,以识别将错误的键/值组合放到TypedMap中的位置。map.put(StringKey.A, 10); // now throws a ClassCastException
因此,我想知道:
map.put(StringKey.A, 10)
不会导致编译错误? 编辑-澄清:
最佳答案
您搞砸了泛型,并以一种不好的方式重载了。您正在扩展HashMap<AbstractKey, Object>
,因此您的类继承了Object put(AbstractKey k, Object v)
方法。在您的类中,您将定义另一个具有不同签名的put
方法,这意味着您只是重载了put
方法,而不是覆盖它。
当您编写map.put(StringKey.A, 10)
时,编译器将尝试查找符合参数类型put(StringKey, Integer)
的方法。您的方法的签名不适用,但是继承的put
的签名适用-StringKey
与AbstractKey
兼容,而Integer
与Object
兼容。因此,它将该代码编译为对HashMap.put
的调用。
解决此问题的方法:将put
重命名为一些自定义名称,例如typedPut
。
顺便说一句,BTW从经验中讲,您的方法非常有趣且引人入胜,但在现实生活中,这样做并不值得。