在Java 1.5中将非泛型List类型转换为泛型List类型

20
我有一个List,保证只包含一种类型的对象。这是由某个我无法更新的库中的一些底层代码创建的。我想基于传入的List对象创建List<ObjectType>,以便我的调用代码正在使用List<ObjectType>。
如何将List(或任何其他对象集合)转换为List<ObjectType> 是最佳方法。
5个回答

19

当与未指定泛型类型参数的遗留代码进行互操作时,请使用通配符。例如,假设您正在调用一个仅返回原始Collection的旧库中的方法:

Collection getItems();

在你的代码中,将结果分配给使用通配符声明的变量:

Collection<?> items = widget.getItems();

这种方式可以保持类型安全,因此您不会收到任何警告。

旧代码可能会指定泛型参数应该是什么(大多数情况下在注释中)。例如:

/**
 * @return the items, as a Collection of {@link Item} instances.
 */
Collection getItems();
在这种情况下,你有选择的余地。你可以将结果转换为一个Collection<Item>,但是如果这样做,你将完全依赖第三方库,并丢弃Java泛型类型的保证:在显式转换时,任何运行时抛出的ClassCastException都会发生。
如果你不完全信任第三方库,但仍需要生成一个Collection<Item>,那么就创建一个新集合,在将其转换为期望类型后添加内容。这样,如果库中存在错误,你会立即发现它,而不是在某些代码远离和更晚的地方神秘地爆炸并出现ClassCastException
例如:
Collection<?> tmp = widget.getItems();
Collection<Item> items = new ArrayList<Item>(tmp.size());
for (Object o : tmp)
  items.add((Item) o); /* Any type error will be discovered here! */

如果类型参数在编译时不知道,您可以使用Collections类的类型检查集合工厂


问题在于,这样做除了消除“原始类型”警告之外,并没有带来任何好处。转换为泛型将意味着利用其优势! - Andrzej Doyle
1
泛型的好处是什么?它不仅仅是让你的代码看起来漂亮,减少了打字!它的好处在于:如果代码编译没有警告,那么您的代码就保证是类型安全的。 就是这样。当您忽略或抑制警告时,您与其不使用泛型不如不用,因为您所做的就是创建一些没有转换运算符的代码,在这种情况下会神秘地引发“ClassCastException”。 - erickson

12
您可以直接对列表进行类型转换:
  List raw = new ArrayList();
  List<String> generic = (List<String>) raw;

2
如果列表确实保持一种类型的项目,那么这是一个好答案。 - Michael Myers
1
那是个非常重要的“如果”。它很大程度上取决于上下文。 - erickson
我可以保证该列表将只包含一种类型的项目。这是在Java 1.4预泛型时编写的代码。 - Shaun
1
一般来说,这不是类型安全的,因为它要求列表是同质的;也就是说,列表中的所有对象都是相同类型的。如果列表包含与其他对象不同类型的对象,则程序可能会在后面抛出ClassCastException异常,而此异常不会指向列表创建的位置。 - Derek Mahar
为了避免我在之前评论中描述的情况,当Eclipse Java编译器遇到从旧列表到泛型列表的转换时,会生成警告:“类型安全性:类型为List的表达式需要未经检查的转换以符合List<String>”。忽略这些编译时警告的程序员将在运行时自食其果。 - Derek Mahar

1
如果你只是在任何地方将其转换为List<T>,你会得到一个"unchecked"编译器警告。我们通过将其移动到一个实用方法中解决了这个问题。
public class Lists {
    @SuppressWarnings({"unchecked"})
    public static <T> List<T> cast(List<?> list) {
        return (List<T>) list;
    }
}

现在调用者不会收到任何警告,例如:

for (Element child : Lists.<Element>cast(parent.getChildren())) {
    // ...
}

checkedList 工具在理论上是一个很好的想法,但实际上必须传入你期望的类非常糟糕。我希望 Java 最终能够获得运行时的泛型信息。


1

最好且最安全的方法是使用java.util.Collections方法'checkedList(List list, Class type)'

使用此方法,您旧列表中的所有项目将尽早进行检查。


Java 5 中的 checkedList 方法是否仅适用于新的(且为空)泛型列表?您是在建议我们在 Java 5 之前使用非泛型的 checkedList 吗?这种遗留形式无法转换为泛型列表。 - Derek Mahar

1

试试这个:

List<ObjectType> objectsWithType = Arrays.asList((ObjectType[])wildcardObjects).toArray());

但请记住,这将产生一个固定长度的列表。如果您尝试从此列表中添加或删除元素,它将抛出错误。因此,在使用 Arrays.asList() 时一定要小心。


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