具有多个值类型的映射,具有泛型的优点 [英] Map with multiple value types with advantages of generics

查看:130
本文介绍了具有多个值类型的映射,具有泛型的优点的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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




  • 将错误的东西编译时间警告

  • 在收集集合时无需投射



所以我想要的是一张地图: p>


  • 它支持多个值对象,

  • 检查输入到映射中的值



  • 基本案例,使用是:

     地图< MyKey,Object> map = new HashMap< MyKey,Object>(); 
    //没有类型检查put();
    map.put(MyKey.A,A);
    map.put(MyKey.B,10);
    //需要从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实现AbstractKey< Integer> {
    C,D;
    }



    我可以创建一个TypedMap,并覆盖put )方法:

      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);

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

      .put(StringKey.A,10); //为什么不会导致编译错误? 
    String a = map.get(StringKey.A); // throws ClassCastException

    如果此.put()可能会导致编译错误,
    作为当前第二好的,我可以得到运行时 ClassCastException 在put()方法中抛出。

      //将此方法添加到AbstractKey接口:
    public Class getValueClass();

    //例如,在StringKey枚举实现中:
    public class getValueClass(){
    return String.class;
    }

    //并覆盖TypedMap中的put()方法:
    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); //现在抛出一个ClassCastException 

    所以,我想知道:




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


    • 这是一个合适的设计,以实现我想要的(见上)?


    • 有没有其他设计可以用来达到我想要的效果?




    EDIT - 说明:




    • 如果你认为这是一个不好的设计 - 你能解释一下为什么吗?

    • 我使用String和Integer作为示例值类型 - 在现实中,我有很多不同的键/值类型对,我想要能够使用。我想在单一地图中使用这些。这是目标。


    解决方案

    与泛型和重载在一个坏的方式。你正在扩展 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 的调用。



    一种解决方法:rename put 添加到一些自定义名称,例如 typedPut



    从经验,你的方法是非常有趣和有吸引力,但在现实生活中它只是不值得的麻烦。


    I want to create a map that will provide the benefits of generics, whilst supporting multiple different types of values. I consider the following to be the two key advantages of generic collections:

    • compile time warnings on putting wrong things into the collection
    • no need to cast when getting things out of collections

    So what I want is a map:

    • which supports multiple value objects,
    • checks values put into the map (preferably at compile-time)
    • knows what object values are when getting from the map.

    The base case, using generics, is:

    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);
    

    I've found a way to resolve the second issue, by creating an AbstractKey, which is generified by the class of values associated with this key:

    public interface AbstractKey<K> {
    }
    public enum StringKey implements AbstractKey<String>{
      A,B;  
    }
    public enum IntegerKey implements AbstractKey<Integer>{
      C,D;
    }
    

    I can then create a TypedMap, and override the put() and get() methods:

    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);
      }
    }
    

    This allows the following:

    TypedMap map = new TypedMap();
    map.put(StringKey.A, "A");
    String a = map.get(StringKey.A);
    

    However, I don't get any compile errors if I put in the wrong value for the key. Instead, I get a runtime ClassCastException on get().

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

    It would be ideal if this .put() could give a compile error. As a current second best, I can get the runtime ClassCastException to be thrown in the put() method.

    // 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);
    }
    

    Now, the ClassCastException is thrown when put into the map, as follows. This is preferable, as it allows easier/faster debugging to identify where an incorrect key/value combination has been put into the TypedMap.

    map.put(StringKey.A, 10); // now throws a ClassCastException
    

    So, I'd like to know:

    • Why doesn't map.put(StringKey.A, 10) cause a compile error?
    • How could I adapt this design to get meaningful compile errors on put, where the value is not of the associated generic type of the key?

    • Is this is a suitable design to achieve what I want (see top)? (Any other thoughts/comments/warnings would also be appreciated...)

    • Are there alternative designs that I could use to achieve what I want?

    EDIT - clarifications:

    • If you think this is a bad design - can you explain why?
    • I've used String and Integer as example value types - in reality I have a multitude of different Key / value type pairs that I would like to be able to use. I want to use these in a single map - that's the objective.

    解决方案

    You are messing with generics and overloading in a bad way. You are extending HashMap<AbstractKey, Object> and so your class is inheriting the method Object put(AbstractKey k, Object v). In your class you are defining another put method with a different signature, which means you are just overloading the put method, instead of overriding it.

    When you write map.put(StringKey.A, 10), the compiler tries to find a method that conforms to the argument types put(StringKey, Integer). Your method's signature doesn't apply, but the inherited put's does -- StringKey is compatible with AbstractKey and Integer is compatible with Object. So it compiles that code as a call to HashMap.put.

    A way to fix this: rename put to some custom name, like typedPut.

    BTW talking from experience your approach is very fun and engaging, but in real life it just isn't worth the trouble.

    这篇关于具有多个值类型的映射,具有泛型的优点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆