例如,假设你有两个类:

public class TestA {}
public class TestB extends TestA{}

我有一个返回List<TestB>的方法,我想将该列表中的所有对象强制转换为TestB,以便最终得到List<TestB>。


当前回答

问题是,如果您的方法包含TestB,那么它不会返回一个teststa列表,那么如果它是正确键入的呢?然后是这个铸件:

class TestA{};
class TestB extends TestA{};
List<? extends TestA> listA;
List<TestB> listB = (List<TestB>) listA;

可以像您所希望的那样工作(Eclipse会警告您进行未检查的强制转换,这正是您正在做的事情,所以meh)。那么你能用这个来解决你的问题吗?事实上你可以这样做,因为:

List<TestA> badlist = null; // Actually contains TestBs, as specified
List<? extends TestA> talist = badlist;  // Umm, works
List<TextB> tblist = (List<TestB>)talist; // TADA!

这正是你想要的,对吧?或者更确切地说:

List<TestB> tblist = (List<TestB>)(List<? extends TestA>) badlist;

对我来说似乎编纂得很好。

其他回答

不过我认为你选错方向了……如果该方法返回一个TestA对象列表,那么将它们强制转换到TestB确实是不安全的。

基本上,您是在要求编译器允许您在不支持TestA类型的TestB上执行操作。

泛型的强制转换是不可能的,但是如果你以另一种方式定义列表,则可以将TestB存储在其中:

List<? extends TestA> myList = new ArrayList<TestA>();

在使用列表中的对象时,仍然需要进行类型检查。

你真的不能*:

示例取自本Java教程

假设有两种类型A和B,使得B扩展了A。 那么下面的代码是正确的:

    B b = new B();
    A a = b;

前面的代码是有效的,因为B是a的子类。 现在,List<A>和List<B>会发生什么?

原来List<B>不是List< a >的子类,因此我们不能写

    List<B> b = new ArrayList<>();
    List<A> a = b; // error, List<B> is not of type List<A>

此外,我们甚至不会写字

    List<B> b = new ArrayList<>();
    List<A> a = (List<A>)b; // error, List<B> is not of type List<A>

*:为了使casting成为可能,我们需要一个共同的父List< a >和List<B>: List<?例如>。以下是有效的:

    List<B> b = new ArrayList<>();
    List<?> t = (List<B>)b;
    List<A> a = (List<A>)t;

但是,您将得到一个警告。你可以通过在你的方法中添加@SuppressWarnings("unchecked")来抑制它。

很奇怪,手动强制转换列表仍然没有提供一些实现如下功能的工具箱:

@SuppressWarnings({ "unchecked", "rawtypes" })
public static <T extends E, E> List<T> cast(List<E> list) {
    return (List) list;
}

当然,这不会逐个检查项,但如果我们清楚地知道我们的实现只提供子类型,那么这正是我们在这里要避免的。

由于这是一个被广泛引用的问题,目前的答案主要解释了为什么它不起作用(或者提出了我永远不希望在生产代码中看到的俗套、危险的解决方案),我认为应该添加另一个答案,展示陷阱和可能的解决方案。


在其他回答中已经指出了一般情况下这不能工作的原因:转换是否实际有效取决于原始列表中包含的对象的类型。当列表中有对象的类型不是TestB类型,而是TestA的不同子类时,则强制转换无效。


当然,类型转换可能是有效的。有时,您拥有编译器无法使用的类型信息。在这些情况下,可以强制转换列表,但通常不建议这样做:

一个人可以……

... 转换整个列表或 ... 强制转换列表中的所有元素

The implications of the first approach (which corresponds to the currently accepted answer) are subtle. It might seem to work properly at the first glance. But if there are wrong types in the input list, then a ClassCastException will be thrown, maybe at a completely different location in the code, and it may be hard to debug this and to find out where the wrong element slipped into the list. The worst problem is that someone might even add the invalid elements after the list has been casted, making debugging even more difficult.

调试这些虚假classcastexception的问题可以通过Collections#checkedCollection系列方法缓解。


基于类型筛选列表

从List<Supertype>转换到List<Subtype>的一种更类型安全的方法是实际筛选列表,并创建一个只包含具有特定类型的元素的新列表。这种方法的实现有一定程度的自由度(例如,关于空项的处理),但一种可能的实现可能是这样的:

/**
 * Filter the given list, and create a new list that only contains
 * the elements that are (subtypes) of the class c
 * 
 * @param listA The input list
 * @param c The class to filter for
 * @return The filtered list
 */
private static <T> List<T> filter(List<?> listA, Class<T> c)
{
    List<T> listB = new ArrayList<T>();
    for (Object a : listA)
    {
        if (c.isInstance(a))
        {
            listB.add(c.cast(a));
        }
    }
    return listB;
}

这个方法可以用来过滤任意列表(不仅仅是关于类型参数的给定子类型-超类型关系),如下例所示:

// A list of type "List<Number>" that actually 
// contains Integer, Double and Float values
List<Number> mixedNumbers = 
    new ArrayList<Number>(Arrays.asList(12, 3.4, 5.6f, 78));

// Filter the list, and create a list that contains
// only the Integer values:
List<Integer> integers = filter(mixedNumbers, Integer.class);

System.out.println(integers); // Prints [12, 78]