Anton Cherkashyn
Anton Cherkashyn

Reputation: 5859

Is it possible to parcel/deparcel a list of different objects that extend same base class?

Say I have a base abstract class(or an interface), called "Base" and several concrete classes that extend it, like Concrete1, Concrete2, Concrete3. Now, I have another Container class that contains a list of "Base" objects:

public class Container implements Parcelable {
  ...
  private List<Base> baseList;

}

Let's say I want to put several concrete objects into baseList:

baseList.add(new Concrete1());
baseList.add(new Concrete2());
baseList.add(new Concrete3());
baseList.add(new Concrete2());

Is it possible to implement parceling for baseList field and have a list of objects of the same types Concrete1, Concrete2, etc. after deparceling? How?

Upvotes: 2

Views: 673

Answers (1)

Paul Boddington
Paul Boddington

Reputation: 37655

I use the following approach.

  1. Make sure that Base implements Parcelable.
  2. Put a describeContents method in Base.
  3. Put a method abstract void write2(Parcel p); method in Base and override it in each concrete class.
  4. Put a method static Base create2(Parcel p) in each concrete class. This should be the reverse of write2.
  5. Write an abstract method abstract int typeNumber() in Base and override it in each concrete subclass (each subclass should return a different value. I use static final fields CONCRETE1, CONCRETE2, ... that I keep in Base).

Then you can add the following to Base:

@Override
public final void writeToParcel(Parcel p, int flags) {
    p.writeInt(typeNumber());
    write2(p);
}

public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {

    @Override
    public Base createFromParcel(Parcel p) {
        switch (p.readInt()) {
            case CONCRETE1: return Concrete1.create2(p);
            case CONCRETE2: return Concrete2.create2(p);
            // etc
            default: throw new AssertionError();
        }
    }

    @Override
    public Base[] newArray(int size) {
         return new Base[size];
    }
}

Then you can write a List using

parcel.writeTypedList(baseList);

and read it back using

List<Base> list = new ArrayList<Base>();
parcel.readTypedList(list, Base.CREATOR);

This approach does not require the names of any classes to be written to the Parcel (it writes int values instead), does not require the CREATOR fields to be found using reflection, and does not require a ClassLoader.

Upvotes: 0

Related Questions