无法从ArrayList强制转换 到ArrayList

问题描述 投票:12回答:4

在我写的一段代码中,我有这一行:

         AllSprites = (ArrayList<ClSprite>) savedInstanceState.getParcelableArrayList("AllSprites");

我收到一个关于从ArrayList<Parcelable>ArrayList<ClSprite>无效演员的错误。为什么这不合法?

java casting
4个回答
7
投票

ArrayList<Derived>施放到ArrayList<Base>或反之亦然是根本不安全的。这样做会在类型系统中打开一个漏洞,如果你尝试这个,Java会在运行时抛出一个ClassCastException

原因是我可以这样做:

ArrayList<Derived> derived = new ArrayList<Derived>();
ArrayList<Base> base = (ArrayList<Derived>) derived; // Not legal!
base.add(new Base()); // Just put a Base into the list, but it only holds Derived!
derived.get(0).doSomethingOnlyInDerived(); // Error!  It's not really a Derived!

顺便说一下,这就是为什么Java在数组之间的隐式转换被破坏以及为什么有ArrayStoreException的原因。在所有情况下,此演员阵容都不安全。


25
投票

一个简单的解决方案是设置返回元素类型

ArrayList<ClSprite> AllSprites = savedInstanceState.<ClSprite>getParcelableArrayList("AllSprites")


17
投票

其他人已经解释了这个问题,但在这种情况下,有一个非常简单的解决方案。只需离开演员表,您的代码就会编译完毕。 :):

ArrayList<ClSprite> AllSprites = savedInstanceState.getParcelableArrayList("AllSprites");

为什么?

看看getParcelableArrayList方法的签名:

public <T extends Parcelable> ArrayList<T> getParcelableArrayList(String key)

这是一个通用方法,其类型参数必须是Parcelable的子元素。如果将它直接分配给这样的变量:

ArrayList<ClSprite> AllSprites; // declaration somewhere
                                // ClSprite implements Parcelable
...

AllSprites = savedInstanceState.getParcelableArrayList("AllSprites");

编译器可以推断出类型参数,所以根本不需要演员!在推导之后,签名看起来像这样:

public ArrayList<ClSprite> getParcelableArrayList(String key)

很明显,我们没有必要从ArrayList<ClSprite>投射到ArrayList<ClSprite>。 :)

但是你为什么会遇到这个错误?如果执行强制转换而不直接将变量赋值给此方法的返回值,则编译器无法推导出类型参数,它只知道返回的类型是ArrayList<Parcelable>。在这种情况下,错误发生在其他人已经解释过的地方。

此外,如果该方法不是通用的,但是像这样:

public ArrayList<Parcelable> getParcelableArrayList(String key)

你无法将返回值赋给AllSprites,因为根本没有类型推论,你也无法从ArrayList<Parcelable>转换为ArrayList<ClSprite>。即使它有意义,Java使用type erasure作为泛型,并使这些事情在运行时不安全。


5
投票

这种演员在Java中是非法的;父母列表无法强制转换为子列表。此外,对ArrayList<X>的演员是危险的,过于严格。你可以通过使AllSprites的类型为List<Parcelable>来解决这两个问题。

© www.soinside.com 2019 - 2024. All rights reserved.