例如,假设你有两个类:

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

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


当前回答

2022年回答

将一组超类型的List转换为一组子类型的List是没有意义的,任何人都不应该尝试或甚至考虑这样做。如果你认为你的代码需要这样做,你需要重写你的代码,使它不需要这样做。

对于这个问题,大多数访问者可能想要做相反的事情,这实际上是有道理的:

将子类型列表转换为超类型列表。

我发现的最好的方法是:

List<TestA> testAs = List.copyOf( testBs );

这样做有以下优点:

这是一个简洁的一行代码 它不会产生警告 如果你的列表是用list .of()创建的,它不会复制!! 最重要的是:它做了正确的事情。

为什么这是对的?

如果你看一下List.copyOf()的源代码,你会发现它的工作原理如下:

如果您的列表是用list .of()创建的,那么它将执行强制转换并返回它,而不复制它。 否则,(例如,如果你的列表是一个ArrayList(),)它将创建一个副本并返回它。

如果List<TestB>是一个<TestB>的数组列表,那么必须创建一个数组列表的副本。如果您将ArrayList<TestB>转换为List<TestA>,则可能会无意中将一个TestA添加到List<TestA>中,这将导致原始ArrayList<TestB>在TestB中包含一个TestA,这是内存破坏:试图迭代原始ArrayList<TestB>中的所有TestB将抛出ClassCastException。

另一方面,如果List<TestB>是使用List.of()创建的,那么它是不可更改的(*1),因此没有人会无意中向其添加一个TestA,因此可以将其强制转换为List<TestA>。


(*1) when these lists were first introduced they were called "immutable"; later they realized that it is wrong to call them immutable, because a collection cannot be immutable, since it cannot vouch for the immutability of the elements that it contains; so they changed the documentation to call them "unmodifiable" instead; however, "unmodifiable" already had a meaning before these lists were introduced, and it meant "an unmodifiable to you view of my list which I am still free to mutate as I please, and the mutations will be very visible to you". So, neither immutable or unmodifiable is correct. I like to call them "superficially immutable" in the sense that they are not deeply immutable, but that may ruffle some feathers, so I just called them "unchangeable" as a compromise.

其他回答

问题是,如果您的方法包含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;

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

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

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

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

当您强制转换对象引用时,您只是强制转换引用的类型,而不是对象的类型。强制转换不会改变对象的实际类型。

Java没有转换对象类型的隐式规则。(与原语)

相反,您需要提供如何将一种类型转换为另一种类型并手动调用它。

public class TestA {}
public class TestB extends TestA{ 
    TestB(TestA testA) {
        // build a TestB from a TestA
    }
}

List<TestA> result = .... 
List<TestB> data = new List<TestB>();
for(TestA testA : result) {
   data.add(new TestB(testA));
}

这比在有直接支持的语言中更冗长,但它是可行的,您不需要经常这样做。

您可以在Eclipse Collections中使用selectInstances方法。这将涉及创建一个新的集合,但不会像使用强制转换的公认解决方案那样有效。

List<CharSequence> parent =
        Arrays.asList("1","2","3", new StringBuffer("4"));
List<String> strings =
        Lists.adapt(parent).selectInstancesOf(String.class);
Assert.assertEquals(Arrays.asList("1","2","3"), strings);

我在示例中加入StringBuffer是为了说明selectInstances不仅向下转换类型,而且还将过滤集合是否包含混合类型。

注意:我是Eclipse Collections的提交者。

简单的答案

你不能直接类型转换。

解决方案

 public <T> List<T> getSubItemList(List<IAdapterImage> superList, Class<T> clazz) {
        return superList.stream()
                .map(item -> clazz.isInstance(item) ? clazz.cast(item) : null)
                .collect(Collectors.toList());
    }

使用

private final List<IAdapterImage> myList = new ArrayList<>();

List<SubType> subTypeList = getSubItemList(myList,SubType.class);

所以这是一个简单的工作,我用来转换我的列表与超级类型到列表与子类型。我们使用的是java 8中引入的流api,在我们的超列表上使用map,我们只是检查传递的参数是否是我们的超类型的实例,返回项目。最后,我们收集成一个新的列表。当然,我们需要把result放到一个新的列表中。