能否对一个泛型类进行分包处理?

19
我正在尝试创建public class MyClass<T extends Parcelable> implements Parcelable。我在实现Parcelable时遇到了麻烦。是否可能创建一个实现Parcelable的通用类呢?(请注意,T也必须实现Parcelable)。
问题出在Parcelable接口需要一个静态(static)变量: public static final Parcelable.Creator <MyParcelable> CREATOR。因此,我无法使用public static final Parcelable.Creator<MyClass<T>> CREATOR,因为MyParcelable<T>是非静态的。
André

相关但不重复:带有约束访问问题的通用类 - Paul Bellora
谢谢。问题是一样的。虽然那里没有提供有效的解决方案,但我无法在该线程上发布评论,因为我没有足够的“声誉”! - Andre Gregori
2
我浏览了你的其他问题并点赞,因为它们写得非常好。副作用是,你现在应该有足够的声望来发表评论了。 - Paul Bellora
4个回答

24

我在使用一个带有泛型的类实现Parcelable时遇到了类似的问题,第一个问题与你遇到的相同:

因此,我不能使用public static final Parcelable.Creator> CREATOR,因为MyParcelable是非静态的。

第二个问题是读取Parcelable对象时需要访问ClassLoader,但由于类型擦除,无法从T中获取它。

下面的类是我正在生产中使用的类的改编版本,可以克服这两个问题。 注意:我没有专门测试过这个类,如果你有任何问题,请告诉我。

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
}

为了让其他人知道,这是我实现它的方式: protected OtherModel(Parcel in) { mTestModel = in.readParcelable(TestModel.class.getClassLoader()); } - MidasLefko
我只是想知道为什么您要调用writeList(items)并遍历列表调用item.writeToParcel(...)?第二次打包难道不是多余的吗? - TheIT
@TheIT 你说得对,谢谢指出!回答已更新。 - Bulwinkel

5
将通用数据成员类名写入包裹中,然后读取它以创建其类加载器。例如:
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);
        }
    }

当存在构造函数参数时,这个是如何工作的? - masterwok

2

是的,您可以这样做。在子类对象构造期间存储类名或类加载器,然后在可包含读写操作期间传递它。

步骤:

步骤1。像这样存储从您的通用类扩展的类名:

public abstract class GenericClass<T> implements Parcelable {
    private String className;

步骤2。任何继承自您的泛型类的类在构造时必须指定类名,如下所示:

public class MyClass extends GenericClass<MyClass> {

    public MyClass () {
        super();
        setClassName(MyClass.class.getName()); // Generic class setter method
    }

第三步。在您的通用类中,您可以像这样读取/写入您的类名称到getClassLoader():

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
    }

}


1
当存在构造函数参数时,这个是如何工作的? - masterwok

0

根据上面的答案,我已经为此创建了扩展函数。

fun <T : Parcelable> Parcel.writeGenericParcelable(data: T, flags: Int) {
    writeString(data::class.java.name)
    writeParcelable(data, flags)
}

fun <T : Parcelable> Parcel.readGenericParcelable(): T {
  val className = readString()!!
  val classNameLoader = Class.forName(className).classLoader
  return readParcelable(classNameLoader)!!
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接