I have the following class which represents a set of properties.
public class Properties
{
/** String type properties. */
private final List<String> m_stringProperties = Arrays.asList("str1", "str2", "str3");
/** Float type properties. */
private final List<String> m_floatProperties = Arrays.asList("float1", "float2", "float3");
/** Integer type properties. */
private final List<String> m_intProperties = Arrays.asList("int1", "int2");
public class PropertyType
{
private final String m_name;
private final Object m_value;
public PropertyType(String name, Object value)
{
m_name = name;
m_value = value;
}
public String getName()
{
return m_name;
}
public Object getValue()
{
return m_value;
}
}
/** The container for the properties. */
private final Map<String, PropertyType> m_properties = new HashMap<>();
/**
* Constructor.
*/
public Properties()
{
}
/**
* Returns the value of the given property if it exists.
*
* @param name The name of the property.
*
* @return The property value if it exists, null otherwise.
*/
public PropertyType getProperty(String name)
{
return m_properties.get(name);
}
/**
* Sets the value of the given property. If the property does not exist, it is added first.
*
* @param name The name of the property.
* @param value The value of the property.
*
* @return None.
*/
public void setProperty(String name, Object value)
{
if ((m_stringProperties.contains(name) && value instanceof String)
|| (m_floatProperties.contains(name) && value instanceof Float)
|| (m_intProperties.contains(name) && value instanceof Integer))
{
m_properties.put(name, new PropertyType(name, value));
}
else
{
assert false : "Invalid property name";
}
}
/**
* Gets the number of properties.
*
* @return The number of properties.
*/
public int count()
{
return m_properties.size();
}
}
Notes
- Each property has a name and a value.
- Property values can be of type String, Float or Integer.
- The names of properties is restricted to the values defined in the list at the top of the class.
- A given property can only be added to the map if it is the correct type for that property name.
The class could be used as follows:
Properties properties = new Properties();
// set properties
properties.setProperty("str1", "testId");
properties.setProperty("float1", 1.0f);
// get properties
Properties.PropertyType str1 = properties.getProperty("str1");
Properties.PropertyType float1 = properties.getProperty("float1");
Properties.PropertyType invalid = properties.getProperty("unknown"); // return null
System.out.println("str1: " + str1.getValue());
System.out.println("float1: " + float1.getValue());
float f1 = (float) properties.getProperty("float1").getValue();
Object o1 = properties.getProperty("float1").getValue();
System.out.println("f1: " + f1);
System.out.println("o1: " + o1);
properties.setProperty("str1", 1.0f); // assertion - str1 property should be String, not Float
I'd like to know if there is a better way to implement this. Specifically, I'd like to avoid the use of Object
and the casting that goes with it. I've experimented with a parametrised class and generic types and even a typesafe heterogenous container as described in Item 29 of Effective Java.
I would like to make it as typesafe as possible - i.e. enforce type checking by the compiler - so that if getProperty
is called the return value is automatically the correct type.
I realise that I could overload setProperty
for each type and that getProperty
could just return an Object
instead of the nested type PropertyType
but that would still leave me with a container of <String, Object>
.
I'm a Java newbie coming from C++. In C++ the map value would be a boost::variant
.