views:

922

answers:

5

I'm trying to have a default java.util.Properties object in my class, with the default properties it accepts, and let the developer override some of them by specifying another java.util.Properties object, but I couldn't find a nice way for doing that.

The intended usage is the following:

Properties defaultProperties = new Properties();
defaultProperties.put("key1", "value1");
defaultProperties.put("key2", "value2");

Properties otherProperties = new Properties();
otherProperties.put("key2", "value3");

Properties finalProperties = new Properties(defaultProperties);

//
// I'd expect to have something like:
// 
// finalProperties.merge(otherProperties);
//
+3  A: 

You're almost good:

Properties defaultProperties = new Properties();
defaultProperties.setProperty("key1", "value1");
defaultProperties.setProperty("key2", "value2");

Properties finalProperties = new Properties(defaultProperties);
finalProperties.setProperty("key2", "value3");

EDIT: replaced put by setProperty.

Jerome
+1  A: 

Yea you're right just invoke the putAll method and you're done.

OscarRyz
CW to avoid having conflicts even thought this was the answer I was about to give
OscarRyz
+7  A: 

java.util.Properties implements the java.util.Map interface, and so you can just treat it as such, and use methods like putAll to add the contents of another Map.

However, if you treat it like a Map, you need to be very careful with this:

new Properties(defaultProperties);

This often catches people out, because it looks like a copy constructor, but it isn't. If you use that constructor, and then call something like keySet() (inherited from its Hashtable superclass), you'll get an empty set, because the Map methods of Properties do not take account of the default Properties object that you passed into the constructor. The defaults are only recognised if you use the methods defined in Properties itself, such as getProperty and propertyNames, among others.

So if you need to merge two Properties objects, it is safer to do this:

Properties merged = new Properties();
merged.putAll(properties1);
merged.putAll(properties2);

This will give you more predictable results, rather than arbitrarily labelling one of them as the "default" property set.

Normally, I would recommend not treating Properties as a Map, because that was (in my opinion) an implementation mistake from the early days of Java (Properties should have contained a Hashtable, not extended it - that was lazy design), but the anemic interface defined in Properties itself doesn't give us many options.

skaffman
Great, I was about to write exactly what you've described. You save me a bug.
Igor
Another danger of seeing Properties as a Map is that it is not "Genericified": `properties.put("key", new Object())` will compile.
Jerome
It will compile, yes, and in some cases it will work fine, as long as you get it out again using `get()`, and don't try and retrieve it using `getProperty()`.
skaffman
Another valuable information here: with properties, always use `setProperty()` and `getProperty()`. Is it right?
Igor
To be safe, yes, but you can get away with `get()` and `put()` if you're sure you know what you're putting in.
skaffman
+2  A: 

Assuming you eventually would like to read the properties from a file, I'd go for loading both files in the same properties object like:

Properties properties = new Properties();
properties.load(getClass().getResourceAsStream("default.properties"));
properties.load(getClass().getResourceAsStream("custom.properties"));
EJB
A: 

putAll(): Copies all of the mappings from the specified map to this hashtable. These mappings will replace any mappings that this hashtable had for any of the keys currently in the specified map.

Blockquote Properties merged = new Properties(); merged.putAll(properties1); merged.putAll(properties2);

Line 2 has no effect at all. None of the properties from the first file will be in the merged properties object.

Volker B.