tags:

views:

121

answers:

3

I have a generic class intended to keep values for different kinds of properties. I want to provide a type-safe way to set property values, so that it is impossible to assign property a value of wrong type.

I defined an interface to be implemented for all property kinds:

public interface Property<T> {

} 

where type parameter T is used to specify the type of property value. Then assuming the class OrderProperty implements this interface properties can be defined like

OrderProperty property1 = new OrderProperty<String>();
OrderProperty property2 = new OrderProperty<Integer>();

Initially I implemented the class to hold property values like

public class Properties<K extends Property> {

    private Map<K, Object> properties = new HashMap<K, Object>();

    public <V> void set(K key, V value) {
        properties.put(key, value);
    }

}

The problem is that the set() method is obviously not type-safe as it does not regard the connection between property and its value type so I could easily write something like

Properties orderProperties = new Properties<OrderProperty>();
OrderProperty countProperty = new OrderProperty<Integer>();
orderProperties.put(countProperty, "1");

and it would compile.

Type-safe implementation would be

public <V> void set(Property<V> key, V value) {
    properties.put(key, value);
}

but of course it will not not compile since key is not of generic type.

I need something like

public <V> void set(K<V> key, V value) {
    properties.put(key, value);
}

but this one is syntactically incorrect.

I am wondering if there is any way to get what I need.

+1  A: 

My guess would be to use something as

public class Properties<V> {
    public void set(Property<V> key, V value) {
        properties.put(key, value);
    }
}

EDIT : Ok, based on your comment, maybe something like this should do it :

public class Properties<V, T extends Property<V>> {
    public void set(T key, V value) {

    }
}

EDIT 2 : To instanciate that class you can do something like

Properties<Integer, OrderedProperty<Integer>> properties = 
    new Properties<Integer, OrderedProperty<Integer>>
Vinze
The problem is that Properties class is to hold properties of a single kind, so it needs to be typed with an implementation of Property interface. This way I can prevent putting say an ItemProperty into the collection of OrderProperties.
fnt
It looks like a good option, but how do I instantiate this Properties class - I mean type parameters? I tried using <? extends Object> but it didn't do - I am probably missing something.
fnt
As I commented at http://stackoverflow.com/questions/3194307/the-use-of-class-type-parameters-in-generic-method/3196582#3196582 Properties should allow for values of different data types contained in Property, i.e. OrderedProperty<Integer>, OrderedProperty<String> and so on.
fnt
+2  A: 

Your Properties class would be able to support only one type of property. This is probably not what you are intending, even if this would work:

public class Properties<V, T extends Property<? extends V>> {
    public void set(T key, V value) {
      //...
    }
}

If you want to support different types of properties, then you must check the validity of your property manually. The reason for this is Java's due to type erasure:

  1. Make your Property<V> aware of the actual type it is going to support
  2. Check for that type in the Properties.set method

 

public interface Property<T> {
  public Class<T> getPropertyType();
}

public class OrderProperty<T> extends Property<T> {
  Class<T> type;
  /** This constructor is required due to type erasure, otherwise the OrderType doesn't know the property type */
  public OrderProperty(Class<T> type) {
    this.type = type;
  }
  public Class<T> getPropertyType() {
    return type;
  }
}

public class Properties<K extends Property> {

    private Map<K, Object> properties = new HashMap<K, Object>();

    public <V> void set(K key, V value) {
        properties.put(key, key.getPropertyType().cast(value));
    }

}
nd
I see that due to type erasure runtime checks based on type parameters are not possible. What I wanted to get was compile-type checks based on provided type parameters. In set() method K and V are sort of "dependent types" so I was wondering if it is possible to define this dependency.While public class Properties<V, T extends Property<? extends V>> { public void set(T key, V value) { //... }}defines such a dependency using class type parameters, it looks like it is impossible to instantiate properties that would allow arbitrary types for V.
fnt
Thanks for your answer anyway, runtime checks are better then no checks at all.
fnt
A: 

EDITED Ok, sorry about that, I didn't fully understand your requirement. In your case, my previous answer was probably pretty useless. Since you want to be able to store different properties in your Properties class and still have the put(..) method be type safe, you could have something like this:

public static class Properties {

    private Map<Property<?>, Object> properties = new HashMap<Property<?>, Object>();

    public <V> void put(Property<V> key, V value) {
        properties.put(key, value);
    }

    @SuppressWarnings("unchecked")
    public <V> V get(Property<V> key) {
        return (V) properties.get(key);
    }

}

In this case, you can only put a property and a value that match the type of that property, like so:

OrderProperty<String> stringProperty = new OrderProperty<String>();
OrderProperty<Integer> countProperty = new OrderProperty<Integer>();

Properties orderProperties = new Properties();
orderProperties.put(countProperty, 3);
orderProperties.put(stringProperty, "");
orderProperties.put(stringProperty, 2);//This will not compile!
Java Drinker
Yes, of course I can create an instance of Properties the way you mentioned, but the matter is that it should allow for properties of different types. So for example OrderProperty<Integer> countProperty = new OrderProperty<Integer>(); orderProperties.put(countProperty, 1); and OrderProperty<String> nameProperty = new OrderProperty<String>(); orderProperties.put(nameProperty, "1"); should be possible at the same time.
fnt
If you look at the whole discussion you will see we are walking in circles ;)Properties class itself should accept type parameter to restrict map keys to specific Property implementation.
fnt