Typesafe access to a generic Map

2010-03-25 by mira

While thinking about the metamodel for td4j, I came up with the idea of having a generic container to allow for storing arbitrary content. A map with key/value pairs seemed to be a good fit for the container.

But I had one additional requirement in mind: I want to have typesafe access to the Map - no casting.

The solution turned out to be fairly simple. I just use a generic interface as the key. The interface is typed with the class of the resource (value) and that way the map accessors (put & get) have typed parameters and return value as well.

The keys have to implement interface ResourceKey. The implementation can be a regular class or also an enum, as shown in the sample code below.


public static void main(String[] args) {
  GenericMap map = new GenericMap();

  map.put(Name.First, "Michael");
  map.put(Name.Last,  "Rauch");
  map.put(YobKey.INSTANCE, 1978);

  String firstName = map.get(Name.First);
  String lastName  = map.get(Name.Last);
  Integer yob = map.get(YobKey.INSTANCE);

  System.out.println(String.format("%s %s (%d)", firstName, lastName, yob));
}

// =================================================================

enum Name implements ResourceKey<String> {
  First, Last;

  public Class<String> getResourceType() {
    return String.class;
  }
}

public static class YobKey implements ResourceKey<Integer> {
  private static YobKey INSTANCE = new YobKey();
  private YobKey () {}		

  public Class<Integer> getResourceType() {
    return Integer.class;
  }
}

// =================================================================

public static class GenericMap {
  private final Map<ResourceKey<?>, Object> resourceMap = new HashMap<ResourceKey<?>, Object>();

  public <T extends Object> void put(ResourceKey<T> key, T res) {
    resourceMap.put(key, res);
  }

  public <T> T get(ResourceKey<T> key) {
    final Object res = resourceMap.get(key);
    return (T) res;
  }

  public Set<ResourceKey<?>> keySet() {
    return resourceMap.keySet();
  }
}

public interface ResourceKey<R> {
  public Class<R> getResourceType();
}

Archive

architecture