Thursday, January 18, 2007

Java Properties and Generics

If you've done much Java programming, you've probably used a Properties object at some point. It's been around since JDK 1.0. It's always been backed by a Hashtable. With Java 5, the Hashtable became genericized, just like all the other Java datastructures. The Properties object was always "genericized" in a way. It's getProperty and setProperty methods only took and returned Strings. So it would seem logical that Properties would extend a Hashtable<String,String>, right?

Nope, it's still a Hashtable<?,?>, i.e. a Hashtable<Object,Object>. Where is all this going? Well I had a class that had a Properties object as a member variable. I had a method that needed to return all the property names of that Properties object. I thought it would make sense for my method to return a Collection<String>. Since Properties extends Hashtable, I could just return it's keySet() method. This is a Set of all the keys in the Hashtable backing the Properties, thus it is a Set of Strings (Set<String>) and thus a Collection<String>. Too easy.

Wrong again. The return type of the keySet() method is a Set<object>, because Properties extends Hashtable<Object,Object>. You cannot case a Set<Object> to a Set<String>. I was very surprised by this. I expected to have throw a @SuppressWarning("unchecked") annotation on my method, but I did not expect that it would be a compilation error. I figure this is because I would be casting a generic interface. I guess. I could do something completely ridiculous:

return (HashSet<String>)new HashSet(myProps.keySet());

This worked with a good 'ol @SuppressWarning("unchecked") annotation. I had to copy my keySet() into a concrete Set object, and then I could cast. Totally ridiculous.

This all came because my Properties object was being injected with Spring. In other words I had a Spring context file with something like:

<property name="myProps">
<props>
<prop key="a">1</prop>

...

I simply replaced this with a map:

<property name="myProps">
<map>
<entry key="a" value="1">
....

I could then make myProps into a Map<String,String> instead of a Properties object, and simply return it's keySet() without any casting. Very nice. So this just brings me back to the original question of why doesn't Properties extend Hashtable<String,String>?

3 comments:

Anonymous said...

this is one of the least explicable Java library warts I've ever come across

I'd love to know if there is a justifiable reason...

Anonymous said...

The only reason I can conceive of is to avoid breaking old code that made (improper) use of the fact that Properties was backed with a Map<Object, Object>.

The real mystery is why did they create Properties by _inheriting_ from a Map instead of just containing one? Composition allows for all the type-safety one could ask for.

Oh well. At this point probably the best we can hope for is that Properties will be deprecated and a new class, done better, will be introduced.

Anonymous said...

Use stringPropertyNames() with Java 6. See also http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6253413