Search code examples
javaandroidparcelableserializable

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


Solution

  • 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