title: Map with multiple value types with advantages of generics
body: <p>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:</p>
  
  <ul>
  <li>compile time warnings on putting wrong things into the collection</li>
  <li>no need to cast when getting things out of collections</li>
  </ul>
  
  <p>So what I want is a map:</p>
  
  <ul>
  <li>which supports multiple value objects, </li>
  <li>checks values put into the map (preferably at compile-time)</li>
  <li>knows what object values are when getting from the map.</li>
  </ul>
  
  <p>The base case, using generics, is:</p>
  
  <pre><code>Map&lt;MyKey, Object&gt; map = new HashMap&lt;MyKey, Object&gt;();
  // 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);
  </code></pre>
  
  <p>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:</p>
  
  <pre><code>public interface AbstractKey&lt;K&gt; {
  }
  public enum StringKey implements AbstractKey&lt;String&gt;{
    A,B;  
  }
  public enum IntegerKey implements AbstractKey&lt;Integer&gt;{
    C,D;
  }
  </code></pre>
  
  <p>I can then create a TypedMap, and override the put() and get() methods:</p>
  
  <pre><code>public class TypedMap extends HashMap&lt;AbstractKey, Object&gt; {
    public &lt;K&gt; K put(AbstractKey&lt;K&gt; key, K value) {
      return (K) super.put(key, value);
    }
    public &lt;K&gt; K get(AbstractKey&lt;K&gt; key){
      return (K) super.get(key);
    }
  }
  </code></pre>
  
  <p>This allows the following:</p>
  
  <pre><code>TypedMap map = new TypedMap();
  map.put(StringKey.A, "A");
  String a = map.get(StringKey.A);
  </code></pre>
  
  <p>However, I don't get any compile errors if I put in the wrong value for the key. Instead, I get a runtime <code>ClassCastException</code> on get().</p>
  
  <pre><code>map.put(StringKey.A, 10); // why doesn't this cause a compile error?
  String a = map.get(StringKey.A); // throws a ClassCastException
  </code></pre>
  
  <p>It would be ideal if this .put() could give a compile error. 
  As a current second best, I can get the runtime <code>ClassCastException</code> to be thrown in the put() method.</p>
  
  <pre><code>// 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 &lt;K&gt; K put(AbstractKey&lt;K&gt; key, K value){
    Object v = key.getValueClass().cast(value);
    return (K) super.put(key, v);
  }
  </code></pre>
  
  <p>Now, the <code>ClassCastException</code> 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.</p>
  
  <pre><code>map.put(StringKey.A, 10); // now throws a ClassCastException
  </code></pre>
  
  <p>So, I'd like to know:</p>
  
  <ul>
  <li>Why doesn't <code>map.put(StringKey.A, 10)</code> cause a compile error?</li>
  <li><p>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?</p></li>
  <li><p>Is this is a suitable design to achieve what I want (see top)? (Any other thoughts/comments/warnings would also be appreciated...)</p></li>
  <li><p>Are there alternative designs that I could use to achieve what I want?</p></li>
  </ul>
  
  <p>EDIT - clarifications:</p>
  
  <ul>
  <li>If you think this is a bad design - can you explain why?</li>
  <li>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.</li>
  </ul>