Generics (List) typing question
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 parseObj开发者_开发技巧ectsToList(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!
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
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.
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);
Think about what you are asking the compiler to do:
- Given a list of "something that is a subtype of
Object
- 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.
精彩评论