title: Java generics - Map of (typed) maps
body: <p>I'm working on a (simple) caching solution of sorts, where a service can request a Cache object from a Map of caches. A Cache object works essentially just like a Map, too, with a key and a value and methods to access and store objects.</p>
  
  <p>I came up with the following solution, but as you can see, it contains a cast (because get() can't know what the types of the nested object are supposed to be).</p>
  
  <pre><code>private final  Map&lt;String, Cache&lt;?, ?&gt;&gt; caches = new HashMap&lt;String, Cache&lt;?, ?&gt;&gt;();
  
  public &lt;K, V&gt; Cache&lt;K, V&gt; getOrCreateCache(String identifier) {
      if (caches.containsKey(identifier)) {
          return (Cache&lt;K, V&gt;) caches.get(identifier);
      } else {
          Cache&lt;K, V&gt; cache = new CacheImpl&lt;K, V&gt;();
          caches.put(identifier, cache);
          return cache;
      }
  }
  
  private void test() {
      Cache&lt;String, String&gt; strCache = getOrCreateCache("string cache");
      strCache.set("key", "value");
  }
  </code></pre>
  
  <p>Now, my questions:</p>
  
  <ul>
  <li>Is this a 'safe' approach, as long as classcastexceptions are handled properly? (probably going to catch those and pack them into a custom exception class)</li>
  <li>Is there a 'safe' alternative? One with generics, if at all possible, because I like them and dislike casts.</li>
  <li>(not directly related) Is this threadsafe? I assume not, but then, I'm no threading expert. Is it enough to just make the whole method synchronized, or would that (with half a dozen clients) cause too much overhead / locking? Is there a neat solution for that?</li>
  </ul>
  
  <p>Edit: Woo, lots of answers, thanks! Editing here to describe an oddity I found while actually testing this:</p>
  
  <pre><code>    Cache&lt;String, String&gt; someCache = service.getOrCreateCache(cacheIdentifier);
      someCache.set("asdf", "sdfa");
      Cache&lt;String, Integer&gt; someCacheRetrievedAgain = service.getOrCreateCache(cacheIdentifier);
      System.out.println(someCacheRetrievedAgain.get("asdf")); // prints "sdfa". No errors whatsoever. Odd.
  </code></pre>