例如,假设你有两个类:
public class TestA {}
public class TestB extends TestA{}
我有一个返回List<TestB>的方法,我想将该列表中的所有对象强制转换为TestB,以便最终得到List<TestB>。
例如,假设你有两个类:
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.
其他回答
class MyClass {
String field;
MyClass(String field) {
this.field = field;
}
}
@Test
public void testTypeCast() {
List<Object> objectList = Arrays.asList(new MyClass("1"), new MyClass("2"));
Class<MyClass> clazz = MyClass.class;
List<MyClass> myClassList = objectList.stream()
.map(clazz::cast)
.collect(Collectors.toList());
assertEquals(objectList.size(), myClassList.size());
assertEquals(objectList, myClassList);
}
这个测试演示了如何将List<Object>转换为List<MyClass>。但是需要注意的是,objectList必须包含与MyClass相同类型的实例。当List<T>使用时,可以考虑这个例子。为此,在构造函数中获取字段Class<T> clazz,并使用它来代替MyClass.class。
更新
实际上,在强类型Java AFAIK中,你不能从超类型转换为子类型。但是可以引入超类型实现的接口。
interface ITest {
}
class TestA implements ITest {
}
class TestB extends TestA {
}
public class Main {
public static void main(String[] args) {
List<ITest> testAList = Arrays.asList(new TestA(), new TestA());
Class<ITest> clazz = ITest.class;
List<ITest> testBList = testAList.stream()
.map(clazz::cast)
.collect(Collectors.toList());
System.out.println(testBList.size());
System.out.println(testAList);
}
}
或者可以从子类型转换为超类型。是这样的:
class TestA {
}
class TestB extends TestA {
}
public class Main {
public static void main(String[] args) {
var a = new TestA();
var b = new TestB();
System.out.println(((TestA) b));
List<TestB> testBList = Arrays.asList(new TestB(), new TestB());
Class<TestA> clazz = TestA.class;
List<TestA> testAList = testBList.stream()
.map(clazz::cast)
.collect(Collectors.toList());
System.out.println(testAList.size());
System.out.println(testAList);
}
}
供参考,我最初的答案指出了与泛型一起使用它的可能性。事实上,我从Hibernate DAO代码中获取了它。
你真的不能*:
示例取自本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")来抑制它。
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.
最安全的方法是实现AbstractList并在实现中强制转换项。我创建了ListUtil helper类:
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
}
很奇怪,手动强制转换列表仍然没有提供一些实现如下功能的工具箱:
@SuppressWarnings({ "unchecked", "rawtypes" })
public static <T extends E, E> List<T> cast(List<E> list) {
return (List) list;
}
当然,这不会逐个检查项,但如果我们清楚地知道我们的实现只提供子类型,那么这正是我们在这里要避免的。