I'm trying to understand why java.util.Properties was implemented in this way. It has two interfaces: getProperty/setProperty which only accepts strings, and put/get which accepts any object as a value. These two interfaces appear to be overlapping, so a string added with put() can be retrieved using getProperty().
There seems to be some problems with this weird hybrid interface. Putting an object that overrides a string property has the side-effect of clearing the string value, producing null as the getProperty result. Adding an integer, or some other value that has a simple string translation, might be misunderstood as being a real property value (but as a property it's always null).
My question is: Is there a real, practical reason for this? Or is it a half-baked implementation as I suspect?
Joshua Bloch mentions this explicitly in Effective Java
[from Chapter 4] In the case of
Properties, the designers intended that only strings be allowed as keys and values, but direct access to the underlyingHashtableallows this invariant to be violated. Once this invariant is violated, it is no longer possible to use other parts of the Properties API (loadandstore). By the time this problem was discovered, it was too late to correct it because clients depended on the use of nonstring keys and values.
That text is in context of using composition over inheritance. He's basically using this as an example of when composition should be used instead of inheritance. If Properties wrapped a Map instead of extending one, it could have enforced the invarient of using String as keys and values.
So the answer is: It was an oversight.
Access to put and get is a result of Properties being an extension of Hashtable, and the two method should not be used (but cannot be hidden from implementation due to their public access in the superclass).
The Javadocs have a nice note about why you shouldn't use those methods, and should instead only use strings:
Because
Propertiesinherits fromHashtable, theputandputAllmethods can be applied to aPropertiesobject. Their use is strongly discouraged as they allow the caller to insert entries whose keys or values are notStrings. ThesetPropertymethod should be used instead. If thestoreorsavemethod is called on a "compromised"Propertiesobject that contains a non-Stringkey or value, the call will fail. Similarly, the call to thepropertyNamesorlistmethod will fail if it is called on a "compromised"Propertiesobject that contains a non-Stringkey.
As @yshavit notes, it'd make more sense for Properties to extend Hashtable<String, String> than a hashtable of two objects, but this was likely a decision made to maintain backwards compatibility, as any programs using get/put with any non-String objects would have been broken by such a change.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With