我正在尝试使用常用技术从Xml创建对象。 (Xml是遗留的,所以尽管已经有了这样的库,但我自己写这个似乎更快。)
我不理解编译器对通用用法的抱怨。代码示例:
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 */
}
感谢您对此主题的任何启发!
答案 0 :(得分:4)
您遇到的问题是,使用您定义的有界通配符,您将无法向集合中添加任何元素。来自this tutorial:
List<? extends Shape >
是有界通配符的示例。的?代表一种未知类型,就像我们之前看到的通配符一样。但是,在这种情况下,我们知道这种未知类型实际上是Shape的子类型。 (注意:它可能是Shape本身,或者是某些子类;它不需要字面上扩展Shape。)我们说Shape是通配符的上限。像往常一样,使用通配符的灵活性需要付出代价。 这个价格是在方法正文中写入形状现在是非法的
答案 1 :(得分:2)
所有通配符类型都意味着您作为T
的第二个参数传递的List
的实际类型参数parseObjectsToList
将成为{{1}的子类型}。这并不意味着相同的Object
将使用不同的类型进行参数化。
现在您有一个List
(名为List<T>
),您正试图致电targetList
。这是非法的,因为targetList.add(Object)
不一定是Object
的子类型。
因为您要添加T
而不是从中提取元素,所以请使用List
并确保这正是您传入的内容。
答案 2 :(得分:1)
使用List<Object>
会有效,但您可能希望更精确地键入List<Object1>
和List<Object2>
以确保其他地方的类型安全。在这种情况下,您需要在将每个对象添加到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)) ;
}
cast()
操作是静态强制转换操作符的反射等价物:(T) newObj
使用改变的方法看起来像这样:
parseObjectsToList("XmlElement1", objectOnes, Object1.class);
答案 3 :(得分:1)
想想你要求编译器做什么:
Object
Object
这没有意义。假设您的列表是Integer
的列表。假设createObjectFromXml
返回String
。允许将String
插入为Integers
键入的列表中是没有意义的。
因此,您可以选择将列表设为List<Object>
,或者找到某种方法使createObjectFromXml
返回特定类型,然后您可以将其与列表类型绑定。