Thursday, June 20, 2013

Typesafe registry of key-value pairs

Have you ever seen or used this pattern of code:

  Map attributes = getAttributes();
  Object value = attributes.get("attr.key")
  if (object instanceof SomeType) {
    SomeType attrValue = (SomeType) value;
    // do something with the value

I've seen the above code and many variations of this pattern scattered all over the various java code bases I've read and it has always nagged me that although we have had the power of generics and the associated type-safety guarantees available at our fingertips for a long time now, this type of general registries of key-value pairs still involve this type of cast-heavy boilerplate.

It stroke me then how often I have I seen just this pattern appear and re-appear so many times that I have all but stopped thinking about it.- "Of course, if you need an extensible registry of values, you use a Map of key-value pairs (or a Dictionary)".

And usually you either engineer your registry around same type of objects or you throw the generic typesafety out with a proverbial bathwater and map your keys (usually of the type String) to values of type, well .. Object.

Would'nt it be nice if we could actually make key-value pairs themselves be type-safe?
One day I got thinking, what would be needed to make this possible.

Given the following general interface:

public interface Registry {
    public interface Key {
        T getValueType();

     V put(Key key, V value);
     V get(Key key)

We could re-write the snippet in the beginning as follows:

    Registry registry = getAttributes();
    SomeType value = registry.get(Keys.ATTR_KEY);
    if (value != null) {
      // do something with the value

Although the difference is not all that huge, the last snippet looks much cleaner and the meaning is much easier to read and understand.

Also, as I've learned to associate explicit type casts with a potential code smell, the latter snippet has a much cleaner feel to it. I know exactly what type of the value I am getting from the registry and the API contract ensures that I will always get the type of the value I am asking for.

To test out this idea, I've started a little project on GitHub. Check it out and let me know what you think...