Search code examples
androidserializationarraylistbundleclasscastexception

Store and retrieve ArrayList of custom serializable class from bundle


I know there are many questions on storing ArrayLists in Bundles here on stackoverflow, but even after reading many of them and the documentation, I do not understand the problem I describe below. I am not so much asking for help with solving this particular programming issue, because I have a "solution", but rather want to understand why I get the error and why my "solution" works.

The standard PointF class is (unfortunately) not serializable, which is why I extend that class and (hopefully correctly) implement the code for the serialization of pointFs as follows:

class PointFSerializable extends PointF implements Serializable
{
    public PointFSerializable(PointF pointF) {
        super(pointF.x, pointF.y);
    }
    private void writeObject(java.io.ObjectOutputStream out) throws IOException {
        out.writeFloat(x);
        out.writeFloat(y);
    }
    private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
        float x = in.readFloat();
        float y = in.readFloat();
        set(x,y);
    }
    private void readObjectNoData() throws ObjectStreamException {
        set(0f,0f);        
    }
}

Now I have a nested ArrayList of such PointFSerializable objects, which is serializable because ArrayList and PointFSerializable are serializable, and which I can hence put into a Bundle bundle as follows:

ArrayList<ArrayList<PointFSerializable>> drawingSerializable = new 
ArrayList<ArrayList<PointFSerializable>>();
  //populate the nested ArrayList
for([...]) {
  ArrayList<PointFSerializable> strokeSerializable = new ArrayList<PointFSerializable>();
  for([...]) {
    strokeSerializable.add(new PointFSerializable(...));
  }
  drawingSerializable.add(strokeSerializable);
}
  //Put it into the Bundle
bundle.putSerializable("Drawing", drawingSerializable);

In a different function I later retrieve the nested ArrayList from the Bundle bundle via getSerializable() and a cast as follows:

ArrayList<ArrayList<PointFSerializable>> drawingSerializable = (ArrayList<ArrayList<PointFSerializable>>)bundle.getSerializable("Drawing");

Up to here everything works as expected, but now I want to loop over the drawingSerializable:

for(ArrayList<PointFSerializable> strokeSerializable : drawingSerializable) {
  for(PointFSerializable pointFSerializable : strokeSerializable) { //<--
    [...]
  }
}

In the line marked with the <-- I get a

java.lang.ClassCastException: android.graphics.PointF cannot be cast to com.cgogolin.myapp.PointFSerializable

which is what I don't understand.

I would have expected this error is strokeSerializable were of tpye ArrayList<PointF>, but, evidently it should not be, because I defined it to be of type ArrayList<PointFSerializable> and if it were not of that type then I would have expected to already get an error in the outer for loop.

Now, in the body of the inner for loop I anyway want to cast the pointFSerializable to PointF. I can hence replace the problematic line by

  for(PointF pointF : strokeSerializable) {

and then everything works as it should. But why?

Why, from bundle.getSerializable("Drawing");, do I apparently getting back something that I can treat like an ArrayList<ArrayList<PointF>> (which is not even a serializable object!) but not like an ArrayList<ArrayList<PointFSerializable>>, even though I put an object of the latter type into the Bundle?


Solution

  • PointF is parcelable, which means you can put the standard PointF into a bundle. If a class is both serializable and parcelable, Android will defer to write and read to a parcel. What is happening is since you are not overriding the parceling method, Android parcels your object as a standard PointF. If you need the ability to serialize your class outside of that, make sure to override the methods required in the Parcelable interface.