Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Casting objects to subclasses of a Parcelable superclass?

Okay, so I have a class SomeClass that is Parcelable.

It has an array of another Parcelable class called SuperClass. In my constructor for SomeClass, I'm trying to read the array of the SuperClass objects into the myArray instance variable. This would be straightforward; however:

SuperClass has two subclasses FirstSubClass and SecondSubClass. The array temp is supposed to have a mixture of all 3, but for some reason it has only SuperClass objects (the if and else if statements don't seem to execute since none of the elements of temp are instances of the subclasses).

public SomeClass(Parcel in) {

    myArray = new SuperClass[100];

    // this is probably where the problem is due to the classloader parameter:
    Object[] temp = in.readArray(SuperClass.class.getClassLoader());

    for(int i = 0; i < myArray.length; i++) {

        if(temp[i] instanceof FirstSubClass)
            myArray[i] = (FirstSubClass) temp[i];

        else if(temp[i] instanceof SecondSubClass)
            myArray[i] = (SecondSubClass) temp[i];

        else
            myArray[i] = (SuperClass) temp[i];
    }

}

What I'm trying to figure out is how I can read the elements of an array of parcelable objects such that the subclasses of this object aren't automatically casted up to the superclass.

(I think I may have worded my question very confusingly, please tell me if I did).

like image 943
u3l Avatar asked Feb 11 '14 17:02

u3l


1 Answers

I tried to reproduce your problem and I couldn't ... I used writeParcelableArray and NOT writeTypedArray. Here are the classes that I've create:

public class SuperClass implements Parcelable {
    public static final Parcelable.Creator<SuperClass> CREATOR = new Creator<SuperClass>() {

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

        @Override
        public SuperClass createFromParcel(Parcel source) {
            return new SuperClass(source);
        }
    };

    private String label;

    public SuperClass() {
    }

    protected SuperClass(Parcel source) {
        label = source.readString();
    }

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

    @Override
    public int describeContents() {
        // TODO Auto-generated method stub
        return 0;
    }
}

And its child classes:

public class FirstSubClass extends SuperClass {
    public static final Parcelable.Creator<FirstSubClass> CREATOR = new Creator<FirstSubClass>() {

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

        @Override
        public FirstSubClass createFromParcel(Parcel source) {
            return new FirstSubClass(source);
        }
    };

    public FirstSubClass() {
        super();
    }

    public FirstSubClass(Parcel source) {
        super(source);
    }

}

and:

public class SecondSubClass extends SuperClass {
    public static final Parcelable.Creator<SecondSubClass> CREATOR = new Creator<SecondSubClass>() {

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

        @Override
        public SecondSubClass createFromParcel(Parcel source) {
            return new SecondSubClass(source);
        }
    };

    public SecondSubClass() {
        super();
    }

    public SecondSubClass(Parcel source) {
        super(source);
    }

}

and the SomeClass:

public class SomeClass implements Parcelable {
    public static final Parcelable.Creator<SomeClass> CREATOR = new Creator<SomeClass>() {

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

        @Override
        public SomeClass createFromParcel(Parcel source) {
            return new SomeClass(source);
        }
    };

    private String name;
    private SuperClass[] array;

    public SomeClass(String name, SuperClass[] array) {
        this.name = name;
        this.array = array;
    }

    protected SomeClass(Parcel source) {
        this.name = source.readString();
        Parcelable[] temp = source.readParcelableArray(SuperClass.class.getClassLoader());
        array = new SuperClass[temp.length];
        for (int i = 0; i < temp.length; i++) {
            array[i] = (SuperClass) temp[i];
        }
    }

    public String toString() {
        StringBuilder sb = new StringBuilder(name);
        if (array == null || array.length == 0) {
            sb.append(" Array is empty");
        } else {
            sb.append(" Array:");
            for (SuperClass sc : array) {
                sb.append(sc.getClass().getSimpleName());
                sb.append(",");
            }
            sb.setLength(sb.length() - 1);
        }
        return sb.toString();
    }

    public String getName() {
        return name;
    }

    public SuperClass[] getArray() {
        return array;
    }

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

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

From an activity I tap a button, create a SomeClass object, attach to an intent that I start a new Activity and from there I log the passed value. The function that starts the activity:

@Override
public void onClick(View v) {
    SuperClass[] array = new SuperClass[] { new SuperClass(), new FirstSubClass(), new SecondSubClass(), new SuperClass() };
    SomeClass cl = new SomeClass("My Label", array);
    Intent intent = new Intent(this, NextActivity.class);
    intent.putExtra("PASS", cl);
    startActivity(intent);
}

and from NextActivity:

public class NextActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //set some content if you wish
        Log.d("LOG_TAG", "Passed value: " + getIntent().getParcelableExtra("PASS"));
    }
}

I can see in the logs:

Passed value: My Label Array:SuperClass,FirstSubClass,SecondSubClass,SuperClass

Exactly what I passed from previous activity ... Check the differences from your code, I suspect you didn't write a CREATOR for FirstSubClass and SecondSubClass

like image 87
gunar Avatar answered Oct 26 '22 04:10

gunar