无法将ArrayList<Parcelable>转换为ArrayList<ClSprite>

12

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

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

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


好的,那你有什么问题? - Karl Knechtel
可能是Java中的泛型的重复问题。 - Thilo
2
看标题,无法将 ArrayList<Parcelable> 强制转换为 ArrayList<ClSprite>。 - Steven Marcus
你的 ArrayList<ClSprite> 应该改为 ArrayList<? extends ClSprite>。这样它就可以适用于 ClSprite 的子类,但是你不能向这样的数组列表中添加新元素。 - Volodymyr Levytskyi
4个回答

26
一个简单的解决方案是将返回元素类型设置为如下所示:

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


5
这是这个问题最简单而又精彩的答案。我想这应该是被接受的答案。 - tasomaniac
它与javac编译器兼容(AndroidStudio使用)。在eclipse中使用的通用转换可以工作,但在Android Studio中不行。应该是被接受的答案。 - bajicdusko
实际上,在getParcelableArrayList方法中不需要类型参数。有关详细信息,请参见我的答案 - WonderCsabo
感谢您,这个 bug 困扰我很久了,我爱你。 - Tintinabulator Zea

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 在泛型方面使用类型擦除,并使其在运行时不安全。


感谢您的详细解释。我曾尝试使用三元运算符为ArrayList<someParcelableClass>设置值,但失败了。这个答案完美地解释了这一点。 - Suau
感谢你,这个 bug 困扰我很久了,我爱你。 - Tintinabulator Zea

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异常。在某些情况下,强制类型转换是不安全的。

5

在Java中,这种转换是非法的;父类列表不能被转换为子类列表。此外,将其强制转换为 ArrayList<X> 是危险和过于限制性的。您可以通过使 AllSprites 的类型为 List<Parcelable> 来解决这两个问题。


我该如何做到这一点,因为我需要在我的自定义类ClSprites中使用这些函数。 - Steven Marcus
你可以在使用每个对象时将其强制转换为 List 中的类型,或者创建一个新的 List<ClSprite> 并在 for 循环中将对象从其他列表中移动。两种解决方案似乎都不是很理想,但恐怕没有更简洁的方法了。 - Ernest Friedman-Hill

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接