我想创建一个映射,该映射将提供泛型的好处,同时支持多种不同类型的值。我认为以下是通用集合的两个主要优点:

  • 编译将错误内容放入集合
  • 的时间警告
  • 从集合中取出东西时无需强制转换

  • 所以我想要一张 map :
  • 支持多个值对象
  • 检查放入映射中的值(最好在编译时)
  • 从 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)不会导致编译错误?
  • 如果值不是键的关联泛型类型,我该如何调整该设计以获取有意义的编译错误?
  • 这是实现我想要的功能的合适设计(请参见顶部)吗? (任何其他想法/评论/警告也将不胜感激...)
  • 是否可以使用其他设计来实现所需的设计?

  • 编辑-澄清:
  • 如果您认为这是一个糟糕的设计-您可以解释原因吗?
  • 我已经使用String和Integer作为示例值类型-实际上,我想使用多种不同的Key/值类型对。我想在一张 map 中使用它们-这就是目标。
  • 最佳答案

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

    当您编写map.put(StringKey.A, 10)时,编译器将尝试查找符合参数类型put(StringKey, Integer)的方法。您的方法的签名不适用,但是继承的put的签名适用-StringKeyAbstractKey兼容,而IntegerObject兼容。因此,它将该代码编译为对HashMap.put的调用。

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

    顺便说一句,BTW从经验中讲,您的方法非常有趣且引人入胜,但在现实生活中,这样做并不值得。

    10-07 19:07
    查看更多