I'm trying to create public class MyClass<T extends Parcelable> implements Parcelable
. I'm having trouble implementing Parcelable
. Is it possible to create a generic class that implements Parcelable
? (Note that T
is bounded so that it also must implement Parcelable
).
I am running into trouble with the fact that the Parcelable interface requires a static variable: public static final Parcelable.Creator<MyParcelable> CREATOR
. Thus I cannot do public static final Parcelable.Creator<MyClass<T>> CREATOR
because MyParcelable<T>
is nonstatic.
André
Generic types are instantiated to form parameterized types by providing actual type arguments that replace the formal type parameters. A class like LinkedList<E> is a generic type, that has a type parameter E .
Yes you can do it.
A Generic Version of the Box Class To update the Box class to use generics, you create a generic type declaration by changing the code "public class Box" to "public class Box<T>". This introduces the type variable, T, that can be used anywhere inside the class.
Generic classes encapsulate operations that are not specific to a particular data type. The most common use for generic classes is with collections like linked lists, hash tables, stacks, queues, trees, and so on.
I had similar issues with implementing Parcelable on a class with a generic, the first issue was the same as what you were experiencing:
Thus I cannot do public static final Parcelable.Creator> CREATOR because MyParcelable is nonstatic.
The second was to read in a Parcelable object you need access to the ClassLoader
which cannot be gotten from T
due to type erasure.
The class below is an adaption of a class I am using in production which overcomes both issues. Note: I have not tested this class specifically, so let me know if you have any issues.
public class TestModel<T extends Parcelable> implements Parcelable {
private List<T> items;
private String someField;
public List<T> items() {
return items;
}
public void setItems(List<T> newValue) {
items = newValue;
}
public String someField() {
return someField;
}
public void setSomeField(String newValue) {
someField = newValue;
}
//region: Parcelable implementation
public TestModel(Parcel in) {
someField = in.readString();
int size = in.readInt();
if (size == 0) {
items = null;
}
else {
Class<?> type = (Class<?>) in.readSerializable();
items = new ArrayList<>(size);
in.readList(items, type.getClassLoader());
}
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(someField);
if (items == null || items.size() == 0)
dest.writeInt(0);
else {
dest.writeInt(items.size());
final Class<?> objectsType = items.get(0).getClass();
dest.writeSerializable(objectsType);
dest.writeList(items);
}
}
public static final Parcelable.Creator<TestModel> CREATOR = new Parcelable.Creator<TestModel>() {
public TestModel createFromParcel(Parcel in) {
return new TestModel(in);
}
public TestModel[] newArray(int size) {
return new TestModel[size];
}
};
//endregion
}
Write the generic data member class name to the parcel and then read it back in order to create its class loader. Example,
public class MyClass<T> implements Parcelable {
T data;
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(data.getClass().getName());
dest.writeParcelable((Parcelable) data, 0);
}
private MyClass(Parcel in) {
final String className = in.readString();
try {
data = in.readParcelable(Class.forName(className).getClassLoader());
} catch (ClassNotFoundException e) {
Log.e("readParcelable", className, e);
}
}
Yes you can. You just need to store the class name or class loader during the construction of your subclass object and then you can pass it during the read/write operation of the parcelable.
Step by step instructions:
Step 1. Store the class name that extends from your Generic class like this:
public abstract class GenericClass<T> implements Parcelable {
private String className;
Step 2. Any classes that extends from your generic class must specify the class name during its construction like this:
public class MyClass extends GenericClass<MyClass> {
public MyClass () {
super();
setClassName(MyClass.class.getName()); // Generic class setter method
}
Step 3. In your generic class, you can then read/write your class names to getClassLoader() like this:
public abstract class GenericClass<T> implements Parcelable {
private String className;
T myGenericObject;
protected MyClass (Parcel in) {
super(in);
this.className = in.readString();
ClassLoader classLoader;
try {
classLoader = Class.forName(this.className).getClassLoader();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
myGenericObject = in.readParcelable(classLoader);
//... Other class members can go here
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeString(className);
//... Other class members can go here
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With