views:

76

answers:

4

I am trying to use a common technique to create objects from Xml. (Xml is legacy, so although there are already libraries to do this, it seemed faster to write this myself.)

I don't understand the compiler's complaint about the generic usage. Code sample:

public void createObjects() {
  List<Object1> objectOnes = new ArrayList<Object1>();
  List<Object2> objectTwos = new ArrayList<Object2>();

  parseObjectsToList("XmlElement1", objectOnes);
  parseObjectsToList("XmlElement2", objectTwos);
}

private void parseObjectsToList(String xmlTag, List<? extends Object> targetList) {
   // read Xml and create object using reflection
   Object newObj = createObjectFromXml(xmlTag);
   targetList.add(newObj)  

/* compiler complains: "The method add(capture#2-of ? extends Object) in the type List<capture#2-of ? extends Object> is not applicable for the arguments (Object)" 
*/

/* If I change method signature to parseObjectsToList(String xmlTag, List targetList)
it works fine, but generates compiler warning about raw type */

}

Thanks for any enlightenment on the subject!

+2  A: 

All a wildcard type means is that the actual type parameter T of the List that you pass as the second argument to parseObjectsToList is going to be a subtype of Object. It does NOT mean that the same List will be parameterized with different types.

So now you have a List<T> (called targetList) and you are trying to call targetList.add(Object). This is illegal because Object is not necessarily a subtype of T.

Because you are adding to the List rather than extracting elements from it, use List<Object> and make sure that's exactly what you pass in.

danben
`List<?>` has the same problem as `List<? extends Object>`. The wildcard means that the `List` has a type parameter, but it's unknown. Therefore, adding any elements to it could "pollute" the collection with elements of the wrong type.
erickson
Yes, that's right. Updated.
danben
Thanks for help - the <?> wildcard usage limitation is a lot clearer now. I ended up doing: parseObjectToConfig(MESSAGE_CONSUMER_TAG, auxiliaryMessageConsumers); // this is List<MessageConsumer> type@SuppressWarnings("unchecked")private boolean parseObjectToConfig(String xmlTagName, List storage) { ... }Since the type of the list is unimportant at this point. The List<Object> didn't work, because compiler didn't like passing strongly typed lists to the method as List<Object>. The solution: ... parseObjectsToList(String tag, List<T> list, Class<? extends T> c) looks good!
Sam Goldberg
+4  A: 

The problem you are running into is that, with the bounded wildcard that you have defined, you will be unable to add any element to the collection. From this tutorial:

List<? extends Shape > is an example of a bounded wildcard. The ? stands for an unknown type, just like the wildcards we saw earlier. However, in this case, we know that this unknown type is in fact a subtype of Shape. (Note: It could be Shape itself, or some subclass; it need not literally extend Shape.) We say that Shape is the upper bound of the wildcard.

There is, as usual, a price to be paid for the flexibility of using wildcards. That price is that it is now illegal to write into shapes in the body of the method

akf
+1  A: 

Using a List<Object> will work, but you might want keep your more precisely typed List<Object1> and List<Object2> for type-safety elsewhere. In that case, you'll need to check the type of each object before adding it to the List.

private void parseObjectsToList(String tag, List<T> list, Class<? extends T> c) {
   // read Xml and create object using reflection
   Object newObj = createObjectFromXml(tag);
   list.add(c.cast(newObj))  ;
}

The cast() operation is a reflective equivalent to the static cast operator: (T) newObj

Using the altered method would look something like this:

parseObjectsToList("XmlElement1", objectOnes, Object1.class);
erickson
+1  A: 

Think about what you are asking the compiler to do:

  1. Given a list of "something that is a subtype of Object
  2. Let me insert an Object into it

This doesn't make sense. Suppose your list is a list of Integer. Suppose that createObjectFromXml returns a String. It wouldn't make sense to allow inserting a String into a list typed for Integers.

So, your options are either to make your List a List<Object> or to find some way to make createObjectFromXml return a specific type, that you can then tie to the type of your list.

davetron5000