Jer*_*gan 231 java generics collections casting list
例如,假设您有两个类:
public class TestA {}
public class TestB extends TestA{}
Run Code Online (Sandbox Code Playgroud)
我有一个返回a的方法,List<TestA>我想将该列表中的所有对象强制转换为TestB最终得到一个List<TestB>.
new*_*cct 560
简单地施展到List<TestB>近乎工作; 但它不起作用,因为你不能将一个参数的泛型类型转换为另一个参数.但是,您可以通过中间通配符类型进行转换,并且允许它(因为您可以使用未选中的警告来转换为通配符类型和通配符类型):
List<TestB> variable = (List<TestB>)(List<?>) collectionOfListA;
Run Code Online (Sandbox Code Playgroud)
Sal*_*dur 113
无法转换泛型,但如果以另一种方式定义列表,则可以在其中存储TestB:
List<? extends TestA> myList = new ArrayList<TestA>();
Run Code Online (Sandbox Code Playgroud)
在使用列表中的对象时,仍然需要进行类型检查.
Ste*_*Kuo 41
你真的不能*:
示例来自此Java教程
假设有两种类型A,B等等B extends A.那么下面的代码是正确的:
B b = new B();
A a = b;
Run Code Online (Sandbox Code Playgroud)
之前的代码是有效的,因为它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>
Run Code Online (Sandbox Code Playgroud)
而且,我们甚至不能写
List<B> b = new ArrayList<>();
List<A> a = (List<A>)b; // error, List<B> is not of type List<A>
Run Code Online (Sandbox Code Playgroud)
*:为了使铸造可能我们需要双方共同的父List<A>和List<B>:List<?>例如.以下内容有效:
List<B> b = new ArrayList<>();
List<?> t = (List<B>)b;
List<A> a = (List<A>)t;
Run Code Online (Sandbox Code Playgroud)
但是,您会收到警告.您可以通过添加@SuppressWarnings("unchecked")到您的方法来抑制它.
fra*_*acz 28
使用Java 8,您实际上可以
List<TestB> variable = collectionOfListA
.stream()
.map(e -> (TestB) e)
.collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)
jer*_*jvl 18
我认为你正朝着错误的方向投射......如果该方法返回一个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;
}
Run Code Online (Sandbox Code Playgroud)
此方法可用于过滤任意列表(不仅具有关于类型参数的给定Subtype-Supertype关系),如下例所示:
// 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]
Run Code Online (Sandbox Code Playgroud)
你可以不投List<TestB>给List<TestA>史蒂夫·郭提及,但你可以转储的内容List<TestA>到List<TestB>.请尝试以下方法:
List<TestA> result = new List<TestA>();
List<TestB> data = new List<TestB>();
result.addAll(data);
Run Code Online (Sandbox Code Playgroud)
我没有尝试过这段代码,所以可能存在错误,但想法是它应该迭代数据对象,将元素(TestB对象)添加到List中.我希望这对你有用.
最好的安全方法是在实现中实现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();
}
};
}
}
Run Code Online (Sandbox Code Playgroud)
您可以使用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
}
Run Code Online (Sandbox Code Playgroud)
小智 5
我知道的唯一方法是复制:
List<TestB> list = new ArrayList<TestB> (
Arrays.asList (
testAList.toArray(new TestB[0])
)
);
Run Code Online (Sandbox Code Playgroud)
将超类型列表转换为子类型列表是无意义的,没有人应该尝试甚至考虑做这样的事情。如果您认为您的代码需要执行此操作,则需要重写您的代码,使其不需要执行此操作。
这个问题的大多数访问者可能想做相反的事情,这实际上是有道理的:
我发现的最好的方法如下:
List<TestA> testAs = List.copyOf( testBs );
Run Code Online (Sandbox Code Playgroud)
这样做有以下优点:
List.of()!!!如果你查看源代码,List.copyOf()你会发现它的工作原理如下:
List.of(),那么它将进行转换并返回它而不复制它。ArrayList(),)它将创建一个副本并返回它。如果您List<TestB>是,则必须制作ArrayList<TestB>一份副本。如果你要转换as ,你将有可能无意中将 a 添加到 that 中,这会导致你的原始数据在 s中包含 a ,这是内存损坏:尝试迭代原始数据中的所有 s会扔一个ArrayList ArrayList<TestB>List<TestA>TestAList<TestA>ArrayList<TestB>TestATestBTestBArrayList<TestB>ClassCastException.
另一方面,如果 yourList<TestB>是使用 创建的List.of(),那么它是不可更改的(*1),因此没有人可以无意中TestA向其添加 a ,因此可以将其强制转换为List<TestA>。
(*1) 当这些列表首次引入时,它们被称为“不可变的”;后来他们意识到称它们为不可变是错误的,因为集合不可能是不可变的,因为它不能保证其包含的元素的不可变性;因此他们更改了文档,将其称为“不可修改”;然而,“不可修改”在这些列表被引入之前就已经有了意义,它意味着“我的列表的对你来说是不可修改的,我仍然可以随意改变,并且这些改变对你来说是非常明显的”。因此,不可变或不可修改都是不正确的。我喜欢称它们为“表面上不可变”,因为它们并不是深度上不可变的,但这可能会激怒一些人,所以作为妥协,我只是称它们为“不变”。
| 归档时间: |
|
| 查看次数: |
233643 次 |
| 最近记录: |