使用具有泛型优势的多种值类型映射

ama*_*ent 5 java generics casting compiler-errors 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);
Run Code Online (Sandbox Code Playgroud)

我找到了一种方法来解决第二个问题,方法是创建一个AbstractKey,它由与此键关联的值类生成:

public interface AbstractKey<K> {
}
public enum StringKey implements AbstractKey<String>{
  A,B;  
}
public enum IntegerKey implements AbstractKey<Integer>{
  C,D;
}
Run Code Online (Sandbox Code Playgroud)

然后我可以创建一个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);
  }
}
Run Code Online (Sandbox Code Playgroud)

这允许以下内容:

TypedMap map = new TypedMap();
map.put(StringKey.A, "A");
String a = map.get(StringKey.A);
Run Code Online (Sandbox Code Playgroud)

但是,如果我为密钥输入了错误的值,我不会收到任何编译错误.相反,我ClassCastException在get()上获得了一个运行时.

map.put(StringKey.A, 10); // why doesn't this cause a compile error?
String a = map.get(StringKey.A); // throws a ClassCastException
Run Code Online (Sandbox Code Playgroud)

如果这个.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);
}
Run Code Online (Sandbox Code Playgroud)

现在,ClassCastException当放入地图时抛出,如下所示.这是首选,因为它允许更容易/更快的调试来识别将不正确的键/值组合放入TypedMap的位置.

map.put(StringKey.A, 10); // now throws a ClassCastException
Run Code Online (Sandbox Code Playgroud)

所以,我想知道:

  • 为什么不map.put(StringKey.A, 10)导致编译错误?
  • 我如何调整此设计以在put上获得有意义的编译错误,其中值不是关键的通用类型的键?

  • 这是一个合适的设计来实现我想要的(见上)?(任何其他想法/意见/警告也将不胜感激......)

  • 有没有我可以用来实现我想要的替代设计?

编辑 - 澄清:

  • 如果你认为这是一个糟糕的设计 - 你能解释一下原因吗?
  • 我已经使用String和Integer作为示例值类型 - 实际上我有许多不同的Key/value类型对,我希望能够使用它们.我想在一张地图中使用这些 - 这就是目标.

Mar*_*nik 3

你正在以一种糟糕的方式搞乱泛型和重载。您正在扩展HashMap<AbstractKey, Object>,因此您的类正在继承该方法Object put(AbstractKey k, Object v)。在您的类中,您正在定义另一个put具有不同签名的方法,这意味着您只是重载该put方法,而不是覆盖它。

当您编写时map.put(StringKey.A, 10),编译器会尝试找到符合参数类型的方法put(StringKey, Integer)。您的方法的签名不适用,但继承的方法的签名适用put-与 兼容并且与 兼容。因此它将该代码编译为对.StringKeyAbstractKeyIntegerObjectHashMap.put

解决此问题的方法是:重命名put为一些自定义名称,例如typedPut.

顺便说一句,根据经验,您的方法非常有趣且引人入胜,但在现实生活中,这不值得麻烦。