Casting objects to subclasses of a Parcelable superclass?
Asked Answered
G

2

6

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).

Gera answered 11/2, 2014 at 17:9 Comment(3)
Is Parcelable an interface or super class? Is that the same as the Parcel that SomeClass takes in its constructor?Erotica
Parcelable is an interface that both SomeClass and SuperClass implement. Parcel is a class.Gera
How do you write to the parcel?Rotenone
R
7

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

Rotenone answered 12/2, 2014 at 5:32 Comment(4)
I have CREATOR variables in all 3 of the classes, so that can't be it. I'll try comparing the rest of your code to my classes; if I still can't find the problem, i'll post my code - thanks for the help!Gera
I found the problem after going through your code a few times! The problem was that I was using the method writeTypedArray rather than writeParcelableArray, which allows for arrays with multiple types of objects to be passed through; could you update your answer to highlight that this is the solution just in case anyone else has this problem?Gera
my Super Class is an abstract Class.. what do you think should i do for my structure..?Whinny
Thanks, nice solution!Dianetics
E
-1

Not sure why you needed to get the classloader in the readArray method but I took a shot and making an example of your code - so there are several pieces of code for the entire example.

Sample Parcelable class - base class for some other in the example

/**
 */
public class Parcelable {

public String parcel() {
    return "in Parcelable.parcel()...";
    }
}

Example SomeClass - changed to force the array to match the hard-code size I returned from Parcel class. If the array contains only those types, then you can just cast to Parcelable instead of having to cast to FirstSubClass, SecondSubClass and SuperClass.

/**
 */
public class SomeClass extends Parcelable {

private Parcelable[] myArray;

public SomeClass(Parcel in) {
    myArray = new SuperClass[3];
    // this is probably where the problem is due to the classloader parameter:
    //Object[] temp = in.readArray(SuperClass.class.getClassLoader());
    Object[] temp = in.readArray();

    /*
    for(int i = 0; i < 3; 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];
    }*/
    for(int i = 0; i < 3; i++) {
        myArray[i] = (Parcelable) temp[i];
    }
}

public String parcel() {
    return "In SomeClass.parcel()...";
}

public void show() {
    for (Parcelable p : myArray) {
        System.out.println (p.parcel());
    }
}

public static void main(String[] args ){
    SomeClass sc = new SomeClass(new Parcel());
    sc.show();
    }
}

Sample Parcel class - had to fake due to no info.

/**
 */
public class Parcel {

public Object[] readArray() {
    Object[] array = new Object[3];
    array[0] = new SuperClass();
    array[1] = new FirstSubClass();
    array[2] = new SecondSubClass();
    return array;
    }
}

Sample SuperClass

/**
 */
public class SuperClass extends Parcelable {

public String parcel() {
    return "In SuperClass.parcel()...";
    }
}

Sample FirtSubClass

/**
 */
public class FirstSubClass extends SuperClass {

public String parcel() {
    return "In FirstSubClass.parcel()...";
    }
}

Sample SecondSubClass

/**
 */
public class SecondSubClass extends SuperClass {

public String parcel() {
    return "In SecondSubClass.parcel()...";
    }
}
Erotica answered 11/2, 2014 at 21:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.