For example, lets say you have two classes:
public class TestA {}
public class TestB extends TestA{}
我有一个方法返回一个List<TestA>
,我想将列表中的所有对象转换为TestB
,以便最终得到一个List<TestB>
。
For example, lets say you have two classes:
public class TestA {}
public class TestB extends TestA{}
我有一个方法返回一个List<TestA>
,我想将列表中的所有对象转换为TestB
,以便最终得到一个List<TestB>
。
直接将类型转换为List<TestB>
几乎可以工作;但是它不起作用,因为您不能将一个参数的泛型类型强制转换为另一个参数。但是,您可以通过中间的通配符类型进行转换,这样就允许了(因为您可以将通配符类型强制转换为其他类型,并带有未检查的警告):
List<TestB> variable = (List<TestB>)(List<?>) collectionOfListA;
泛型类型的强制转换是不可能的,但如果你用另一种方式定义列表,就可以将TestB
存储在其中:
List<? extends TestA> myList = new ArrayList<TestA>();
当您使用列表中的对象时,仍然需要进行类型检查。
Cannot instantiate the type ArrayList<? extends TestA>
的错误。 - Nateowami使用Java 8,您实际上可以
List<TestB> variable = collectionOfListA
.stream()
.map(e -> (TestB) e)
.collect(Collectors.toList());
new List<TestB>
、循环和转换来做到这一点吗? - Patrick M你真的不能*:
示例来自这个Java教程
假设有两种类型A
和B
,使得B extends 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>
List<A>
和 List<B>
,比如 List<?>
。以下是有效的示例: List<B> b = new ArrayList<>();
List<?> t = (List<B>)b;
List<A> a = (List<A>)t;
然而,你会收到一个警告。你可以通过在方法中添加 @SuppressWarnings("unchecked")
来抑制它。
我认为您的转换方向不正确......如果方法返回一个TestA
对象列表,那么将它们强制转换为TestB
是不安全的。
基本上,您正在请求编译器让您对不支持TestB
操作的类型TestA
执行它们。
由于这个问题被广泛引用,目前的答案主要解释了为什么它不起作用(或提出了我永远不想在生产代码中看到的hacky,危险的解决方案),因此我认为添加另一个答案以显示陷阱和可能的解决方法是合适的。
为什么通常情况下这种方法不起作用已经在其他答案中指出:是否可以进行转换实际上取决于原始列表中包含的对象的类型。当列表中存在其类型不属于TestB
类型,而是其它TestA
字类时,强制类型转换就无效。
当然,强制类型转换可能有效。有时您拥有有关类型的信息,但编译器无法获取该信息。在这些情况下,可以进行列表转换,但通常不建议这样做:
可以选择以下两种方式之一...
第一种方法(当前接受的答案)的影响是微妙的。乍一看似乎可以正常工作。但是如果输入列表中有错误的类型,则会抛出ClassCastException
,可能在代码中完全不同的位置,并且很难调试和找出哪个错误元素滑入了列表。最糟糕的问题是有人甚至可能在列表被强制转换后添加无效元素,使得调试变得更加困难。
解决这些虚假ClassCastExceptions
的问题可以通过使用集合框架中的Collections#checkedCollection
方法系列来缓解。
从List<Supertype>
转换为 List<Subtype>
的更安全的方式是实际上过滤列表,并创建一个包含仅具有特定类型元素的新列表。对于此类方法的实现(例如,关于null
条目的处理),存在一些灵活性,但是可能的一种实现如下:
/**
* 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]
正如Steve Kuo所提到的,您无法将List<TestB>
转换为List<TestA>
,但是您可以将List<TestA>
的内容转储到List<TestB>
中。请尝试以下操作:
List<TestA> result = new List<TestA>();
List<TestB> data = new List<TestB>();
result.addAll(data);
我没有尝试过这段代码,所以可能会有错误,但是它的思路是遍历数据对象,并将元素(TestB 对象)添加到列表中。希望对你有用。
最安全的方法是实现一个 AbstractList
并在实现中转换项目。我创建了一个 ListUtil
帮助类:
public class ListUtil
{
public static <TCastTo, TCastFrom extends TCastTo> List<TCastTo> convert(final List<TCastFrom> list)
{
return new AbstractList<TCastTo>() {
@Override
public TCastTo get(int i)
{
return list.get(i);
}
@Override
public int size()
{
return list.size();
}
};
}
public static <TCastTo, TCastFrom> List<TCastTo> cast(final List<TCastFrom> list)
{
return new AbstractList<TCastTo>() {
@Override
public TCastTo get(int i)
{
return (TCastTo)list.get(i);
}
@Override
public int size()
{
return list.size();
}
};
}
}
cast
方法来盲目地将列表中的对象转换,而使用convert
方法进行安全转换。
例如:void test(List<TestA> listA, List<TestB> listB)
{
List<TestB> castedB = ListUtil.cast(listA); // all items are blindly casted
List<TestB> convertedB = ListUtil.<TestB, TestA>convert(listA); // wrong cause TestA does not extend TestB
List<TestA> convertedA = ListUtil.<TestA, TestB>convert(listB); // OK all items are safely casted
}
List<TestB> list = new ArrayList<TestB> (
Arrays.asList (
testAList.toArray(new TestB[0])
)
);
将一个超类型的列表转换为子类型的列表是毫无意义的,没有人应该尝试或者考虑这样做。如果你认为你的代码需要这样做,你需要重写你的代码,让它不需要这样做。
大多数访问这个问题的人可能希望做相反的事情,这实际上是有意义的:
我发现最好的方法如下:
List<TestA> testAs = List.copyOf( testBs );
这样做有以下优点:
List.of()
创建的,则不会复制 !!!如果您查看 List.copyOf()
的源代码,您会发现它的工作方式如下:
List.of()
创建的,则进行类型转换并返回而不复制它。ArrayList()
,)它将创建一个副本并返回它。如果您的 List<TestB>
是一个 ArrayList<TestB>
,那么必须制作 ArrayList
的副本。如果您将 ArrayList<TestB>
强制转换为 List<TestA>
,则可能会无意中将 TestA
添加到该 List<TestA>
中,然后导致您的原始 ArrayList<TestB>
包含一个 TestA
,这是内存破坏:尝试迭代原始 ArrayList<TestB>
中的所有 TestB
将抛出一个 ClassCastException
。
另一方面,如果您的 List<TestB>
是使用 List.of()
创建的,则它是不可更改的(*1),因此没有人会无意中向其中添加 TestA
,因此只需将其强制转换为 List<TestA>
即可。
(*1) 当首次引入这些列表时,它们被称为“不可变的”;后来他们意识到称它们为不可变是错误的,因为集合不能被视为不可变,因为它不能保证其包含的元素的不可变性;因此,他们更改了文档以将它们称为“不可修改的”。然而,“不可修改的”在这些列表引入之前就已经有了一种含义,它意味着“我的列表的一种对你不可修改的视图,但我仍然可以随意进行修改,并且这些修改对你非常明显”。因此,既不是不可变的也不是不可修改的都是正确的。我喜欢称它们为“表面上不可变”,意思是它们不是深度不可变的,但这可能会引起一些麻烦,所以我将它们称为“unchangeable”,作为一个妥协。
List<TestB> variable = (List<TestB>)(Object) collectionOfListA;
有什么不同吗? - wlnirvana