Abstract class as parcelable

FranBran picture FranBran · Mar 22, 2014 · Viewed 10.3k times · Source

Basicly I have the following structure in my app:

uml of my app

It would be straightforward to implement such a structure without the abstract class ProjectItem, but in this case I don't know how to implement this.

The abstract class ProjectItem needs a CREATOR as it should be parcelable. (like in.readTypedList(mProjectItems, ProjectItem.CREATOR); within the constructor Project(Parcel in))

But in fact, the CREATOR can only be implemented in its derived classes for logical reasons.

So, how to implement this structure in order to keep the class Project parcelable??

Edit

This is what one of the constructors of Project looks like:

private Project(Parcel in) {
    in.readTypedList(mProjectItems, ProjectItem.CREATOR);
}

But as I already said, ProjectItem shouldn't have to implement a CREATOR

Answer

Fletcher Johns picture Fletcher Johns · Oct 6, 2014

My solution is similar to evertvandenbruel's. But I identify the concrete class using an int so that I can use a switch block. I also have that switch block in a static getConcreteClass(Parcel) method.

AbstractClass.java

public abstract class AbstractClass implements Parcelable {

public static final int CLASS_TYPE_ONE = 1;
public static final int CLASS_TYPE_TWO = 2;

public static final Creator<AbstractClass> CREATOR = new Creator<AbstractClass>() {
    @Override
    public AbstractClass createFromParcel(Parcel source) {

        return AbstractClass.getConcreteClass(source);
    }

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

protected String mAbstractClassString;

public AbstractClass(String abstractClassString) {
    mAbstractClassString = abstractClassString;
}

public AbstractClass(Parcel source) {
    mAbstractClassString = source.readString();
}

public static AbstractClass getConcreteClass(Parcel source) {

    switch (source.readInt()) {
        case CLASS_TYPE_ONE:
            return new ConcreteClassOne(source);
        case CLASS_TYPE_TWO:
            return new ConcreteClassTwo(source);
        default:
            return null;
    }
}

@Override
public int describeContents() {
    return 0;
}

@Override
public void writeToParcel(Parcel dest, int flags) {
    dest.writeString(mAbstractClassString);
}

@Override
public String toString() {
    return "Parent String: " + mAbstractClassString + '\n';
}
}

ConcreteClassOne.java

public class ConcreteClassOne extends AbstractClass {

private String mString;

public ConcreteClassOne(String abstractClassMemberString, String string) {
    super(abstractClassMemberString);

    mString = string;
}

public ConcreteClassOne(Parcel source) {
    super(source);
    mString = source.readString();
}

@Override
public void writeToParcel(Parcel dest, int flags) {

    dest.writeInt(CLASS_TYPE_ONE);
    super.writeToParcel(dest, flags);
    dest.writeString(mString);
}

@Override
public String toString() {
    return super.toString().concat("Child String: " + mString);
}
}

ConcreteClassTwo.java

public class ConcreteClassTwo extends AbstractClass {

private String mString;
private int mInt;

public ConcreteClassTwo(String abstractClassString, String string, int anInt) {
    super(abstractClassString);
    mString = string;
    mInt = anInt;
}

public ConcreteClassTwo(Parcel source) {
    super(source);
    mString = source.readString();
    mInt = source.readInt();
}

@Override
public void writeToParcel(Parcel dest, int flags) {

    dest.writeInt(CLASS_TYPE_TWO);
    super.writeToParcel(dest, flags);
    dest.writeString(mString);
    dest.writeInt(mInt);
}

@Override
public String toString() {

    String string = super.toString();
    for (int i = 0; i < mInt; i++) {
        string = string.concat("Child String: " + mString + '\n');
    }
    return string;
}
}