views:

222

answers:

5

I have the following code

private Map<KEY, Object> values = new HashMap<KEY, Object>();

public void set(KEY key, Object value) {
 values.put(key, value);
}

private Object getObj(KEY key) {
 return values.get(key) == null ? key.getDefaultValue() : values.get(key);
}

public List<E> getList(KEY key) {
 return (List<E>) getObj(key);
}

The idea to be able to send in any Objects in a map and then retrieve it, but how do I solve the genereics-part in java?

Here is an example code. Here I save the List into the map:

 List<String> list = new ArrayList<String>();

 list.add("a string");

 session.set(KEY.LIST, list);

Now I want to get the List again:

List<String> list = session.getList(KEY.LIST);

But now I get a

cannot convert from List<E> to List<String>

error for obvious reasons.

Is it possible to get a List(String) from the method, without any typecasts? Is this method correct?

public List<E> getList(KEY key) {
 return (List<E>) getObj(key);
}
+2  A: 

The cast you've given will generate a warning, because it's not really checking anything other than that getObj returns a List. In fact, there's nothing you can really check efficiently, due to type erasure - at execution time, you'll have a List which happens to contain strings, but that's all.

You could certainly check that everything in the list is a string (or null) but that's an O(n) operation. Or you could just do the cast, suppress the warning and hope... in which case you'll only get an exception if and when you hit a non-string.

Jon Skeet
+2  A: 

You can't solve that because of type erasure. The best you can do is to supress the warning, if you are absolutely sure that the cast can't go wrong.

JG
A: 

Instead of putting a List in the session, introduce a type that makes sense in the application domain. For example instead of List<LineItem>, use Order:

public final class Order {
    private final List<LineItem> lineItems;
    [...]
}
Tom Hawtin - tackline
+1  A: 

You could try a MultiMap. You can store multiple values per key. Google has an implementation. Apache commons probably has an implementation also.

Trevor Harrison
A: 

Another option to overcome type erasure* is to pass a reference to the object class that you are expecting in the return.

You also get a chance to do some type checking.

public <E> List<E> getList(KEY key, Class<? extends E> elementClass)
{
  Object o = getObj(key);
  if ( ! (o instanceof List) ) throw new RuntimeException("Not a list value");
  List l = (List)o;
  if ( l.size() > 0 && !elementClass.isAssignableFrom( l.get(0) ) ) throw new RuntimeException("List element not valid type");

  return (List<E>)l;
}

You would call this like:

List<String> list = session.getList(KEY.LIST, String.class);

*at least overcome type erasure in the declaration of the method. You still have to do some casting in the method body.

Trevor Harrison