泛型(列表)类型问题

3
我将尝试使用一种常见的技术从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 */

}

感谢您对此主题的任何启示!
4个回答

4
您遇到的问题是,使用您定义的有界通配符,您将无法向集合中添加任何元素。来自此教程

List<? extends Shape > 是有界通配符的示例。 ? 代表一个未知类型,就像我们之前看到的通配符一样。但是,在这种情况下,我们知道这个未知类型实际上是Shape的子类型。 (注意:它可以是Shape本身或某个子类;它不必字面上扩展Shape。)我们说Shape是通配符的上限。

像往常一样,使用通配符的灵活性需要付出代价。 这个代价是现在在方法体中写入shapes是非法的


2
所有通配符类型的意思是,你传递给 parseObjectsToList 的第二个参数 List 的实际类型参数 T 将是 Object 的子类型。这并不意味着同一个 List 将被参数化为不同的类型。
现在你有一个名为 targetListList<T>,并且你试图调用 targetList.add(Object)。这是不合法的,因为 Object 不一定是 T 的子类型。
因为你正在向 List 添加元素而不是从中提取元素,请使用 List<Object> 并确保恰好传入此类型。

List<?>List<? extends Object> 存在相同的问题。通配符意味着 List 具有类型参数,但是该参数是未知的。因此,向其中添加任何元素都可能会“污染”集合并导致错误类型的元素存在。 - erickson
感谢帮忙 - 现在<?>通配符的使用限制更清楚了。最终我做成了这样:parseObjectToConfig(MESSAGE_CONSUMER_TAG,auxiliaryMessageConsumers); // 这是List <MessageConsumer>类型@SuppressWarnings("unchecked") private boolean parseObjectToConfig(String xmlTagName, List storage) { ... }由于列表的类型在此时不重要。 List<Object>无法工作,因为编译器不喜欢将强类型列表作为List <Object>传递给方法。解决方案: ... parseObjectsToList(String tag,List <T> list,Class <? extends T> c)看起来不错! - Sam Goldberg

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);

1

考虑一下你要求编译器做什么:

  1. 给出一个“某个属于Object子类型的列表”
  2. 让我在其中插入一个Object

这没有意义。假设你的列表是Integer的列表。假设createObjectFromXml返回一个String。允许将String插入到为Integers类型而定义的列表中是没有意义的。

因此,你的选择要么是将你的List变成List<Object>,要么找到一种方法使createObjectFromXml返回一个特定的类型,然后将其与你的列表类型绑定。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接