Okay, I spent a number of hours battling with Guice last night and found a simple bug in the Guice code, which is probably more a bug in Sun’s JVM than anything else. I don’t blame the Guice guys for causing my pain and suffering, I fully blame Sun. Looking at the JavaDoc of the Properties class, where the issue exists, we see it was written by Arthur van Hoff, Michael McCloskey and Xueming Shen. So, I have little choice but to blame them.
Okay, first off, let’s discuss the problem. Properties implements Map. In fact it extends Hashtable. The only problem is that it isn’t really Map unless you have generics and I mean true generics. Furthermore, since I not a fan of the generic Map since it still lets in Object on the get, containsValue, containsKey and remove methods, even if Properties implemented this interface correctly, it isn’t really all that safe from runtime issues. Properties is really a hash for Strings only. It also defines a hierarchy of String key-value-pairs wherein you can define default values using a tree like structure. The only problem is that Map doesn’t really do that so well. Not to mention Map has no concept of default values at all.
EDIT: how about an example
Properties defProps = new Properties(); defProps.setProperty("foo", "fooValue"); Properties myProps = new Properties(defProps); myProps.setProperty("bar", "barValue"); Set<String> keys = myProps.keySet(); System.out.println("Does foo key exists? " + keys.contains("foo")); Output: Does foo key exists? false
The issue I hit was that the Guice dudes where calling the entrySet method, which is defined on Map and implemented in Hashtable. Properties really should override EVERY method in Hashtable if it were to truly implement the Map interface as best it could. But guess what, it doesn’t even come close. So, if you use any of the methods not specifically defined on the Properties class, you are guaranteed to NOT get the correct results because it will not contain any of the defaults you might have setup. Defaults are only handled via composition because the Properties object contains a pointer to another Properties object that contains the defaults. This means that it has to find properties using a lookup method that realizes Properties are defined in a tree like structure.
So why doesn’t Sun just fix this f&*#ing thing (this post has been filtered by the Inversoft Profanity Filter BTW ;)? They can’t. Whoever wrote this originally pretty much ensured that this horrible abomination of a class could never be fixed. In order to ensure backwards serial compatibility, the default Properties must be a member variable. They can’t just go adding serialization methods or externalizable methods, because you can’t do that and keep it compatible. They can’t change the parent class or any member variables since that jacks up serialization and compile/runtime behavior. So, you are left with this horrible class that can never be fixed.
Okay, I’ve pretty much flamed Sun and these three engineers, who might not have actually written the original class and now I’d like to offer a suggestion to not look like I’m not constructive. Deprecate this class and write it correctly. Make the new one called PropertiesTree or something like that and implement it right this time. And a quick note to Sun: “Let me know if you need any help rewriting this.”