12

In a piece of code I've written, I have this line:

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

I'm getting an error about an invalid cast from an ArrayList<Parcelable> to ArrayList<ClSprite>. Why isn't this legal?

4
  • ... Okay, so what's your question? Commented Aug 5, 2011 at 4:15
  • possible duplicate of Generics in Java Commented Aug 5, 2011 at 4:18
  • 2
    Look at the title, Cannot cast from ArrayList<Parcelable> to ArrayList<ClSprite> Commented Aug 5, 2011 at 4:48
  • Your ArrayList<ClSprite> should be changed to ArrayList<? extends ClSprite>. Then it will work for subclasses of ClSprite but you can't add new elements to such arraylist Commented Jul 24, 2013 at 18:00

4 Answers 4

26

A simple solution is to set the returning element type like so

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

Sign up to request clarification or add additional context in comments.

3 Comments

This is the simplest and brilliant answer to this problem. This should be the accepted answer I guess.
And it is compatible with javac compiler (used by AndroidStudio). Generic casting i used works in eclipse, but not in Android Studio. Should be accepted answer.
Actually the type parameter is not needed at the getParcelableArrayList method. See my answer for details.
17

Others already explained the problem, but in this case, there is a very simple solution for it. Simply leave the cast, and your code will compile. :) :

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

Why?

Take a look at the signature of the getParcelableArrayList method:

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

It's a generic method whose type parameter must be a child of Parcelable. If you assign it directly to a variable like this:

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

AllSprites = savedInstanceState.getParcelableArrayList("AllSprites");

the compiler can deduce the type parameter, so there is no need the cast at all! After deducing, the signature would look like this:

public ArrayList<ClSprite> getParcelableArrayList(String key)

It is clear the we do not have to cast from ArrayList<ClSprite> to ArrayList<ClSprite>. :)

But why did you got this error? If you perform a cast and not assign the variable directly to the return value of this method, the compiler cannot deduce the type parameter, it only knows that the returned type is ArrayList<Parcelable>. And in this case, the error takes places what the others already explained.

Also if the method would not be generic, but like this:

public ArrayList<Parcelable> getParcelableArrayList(String key)

you could not assign the return value to AllSprites, because there is no type deduction at all, and you cannot convert from ArrayList<Parcelable> to ArrayList<ClSprite>. Even though it would make sense, Java uses type erasure for generics, and makes these things unsafe at runtime.

1 Comment

thanks for the great explanation, i tried to set the value for an ArrayList<someParcelableClass> using a ternary operator, which doesn't work. And this answer explains it perfectly.
7

It is fundamentally unsafe to cast an ArrayList<Derived> to an ArrayList<Base> or vice-versa. Doing so opens up a hole in the type system, and Java will throw a ClassCastException at runtime if you try this.

The reason is that I could do something like this:

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!

This is the reason, by the way, that Java's implicit conversions between arrays are broken and why there's ArrayStoreException. This cast isn't safe under all cases.

Comments

5

That cast is simply illegal in Java; a list-of-parent can't be cast to a list-of-child. Furthermore, the cast to ArrayList<X> is dangerous and overly restrictive. You could fix both problems by making the type of AllSprites be List<Parcelable>.

2 Comments

How would I do this as I need the functions in my custom class, ClSprites.
You could just cast each object in the List as you use it, or you could create a new List<ClSprite> and move the objects from the other list in a for loop. Neither solution seems ideal but I'm afraid there's no cleaner way.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.