You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

泛型抽象基类子类列表的Parcelable序列化/反序列化问题

泛型基类Base<T>的Parcelable实现及列表打包解包方案

一、Parcelable接口实现位置与CREATOR的放置

  • 抽象基类Base<T>需要实现Parcelable接口,用来处理所有子类通用的打包/解包逻辑(比如通用成员变量的读写)。但由于T mData的具体类型只有子类才知道,基类无法直接处理它的序列化,所以必须在基类中定义抽象方法,让子类实现mData的具体读写逻辑:
    protected abstract void writeDataToParcel(Parcel dest, int flags);
    protected abstract void readDataFromParcel(Parcel in);
    
  • 每个子类(如Sub1、Sub2)必须实现上述抽象方法,针对自身的T类型完成mData的打包和解包。同时,CREATOR必须放在每个子类中——因为基类是抽象类无法实例化,而每个子类是具体类,需要自己的CREATOR来生成实例。

代码示例

基类Base<T>

public abstract class Base<T> implements Parcelable {
    protected T mData;
    protected String commonField; // 通用示例字段

    // 普通构造方法
    public Base(String commonField) {
        this.commonField = commonField;
    }

    // 解包专用构造方法,调用子类的readDataFromParcel
    protected Base(Parcel in) {
        this.commonField = in.readString();
        readDataFromParcel(in);
    }

    // 子类需实现:写入mData到Parcel
    protected abstract void writeDataToParcel(Parcel dest, int flags);
    // 子类需实现:从Parcel读取mData
    protected abstract void readDataFromParcel(Parcel in);

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        // 先写通用字段
        dest.writeString(commonField);
        // 再让子类写自己的mData
        writeDataToParcel(dest, flags);
    }
}

子类Sub1(T=byte[])

public class Sub1 extends Base<byte[]> {
    public Sub1(String commonField, byte[] data) {
        super(commonField);
        this.mData = data;
    }

    protected Sub1(Parcel in) {
        super(in);
    }

    @Override
    protected void writeDataToParcel(Parcel dest, int flags) {
        dest.writeByteArray(mData);
    }

    @Override
    protected void readDataFromParcel(Parcel in) {
        this.mData = in.createByteArray();
    }

    // 子类专属的CREATOR
    public static final Creator<Sub1> CREATOR = new Creator<Sub1>() {
        @Override
        public Sub1 createFromParcel(Parcel in) {
            return new Sub1(in);
        }

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

子类Sub2(T=Object,假设Object实现Serializable)

public class Sub2 extends Base<Object> {
    public Sub2(String commonField, Object data) {
        super(commonField);
        this.mData = data;
    }

    protected Sub2(Parcel in) {
        super(in);
    }

    @Override
    protected void writeDataToParcel(Parcel dest, int flags) {
        dest.writeSerializable(mData);
    }

    @Override
    protected void readDataFromParcel(Parcel in) {
        this.mData = in.readSerializable();
    }

    public static final Creator<Sub2> CREATOR = new Creator<Sub2>() {
        @Override
        public Sub2 createFromParcel(Parcel in) {
            return new Sub2(in);
        }

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

二、List<Base<T>>的整体打包与解包

由于列表中包含不同子类的实例,打包时需要先标记每个实例的类型,解包时根据类型找到对应的CREATOR来实例化对象。这里提供两种方案:

方案1:使用枚举标记类型(推荐,无反射更安全高效)

打包逻辑

// 先定义类型枚举
enum InstanceType { SUB1, SUB2 }

public void writeBaseList(Parcel dest, List<Base<?>> instancesList) {
    // 先写入列表长度
    dest.writeInt(instancesList.size());
    for (Base<?> instance : instancesList) {
        // 写入实例类型标记
        if (instance instanceof Sub1) {
            dest.writeInt(InstanceType.SUB1.ordinal());
        } else if (instance instanceof Sub2) {
            dest.writeInt(InstanceType.SUB2.ordinal());
        }
        // 写入实例本身
        dest.writeParcelable(instance, 0);
    }
}

解包逻辑

public List<Base<?>> readBaseList(Parcel in) {
    List<Base<?>> list = new ArrayList<>();
    int size = in.readInt();
    for (int i = 0; i < size; i++) {
        int typeOrdinal = in.readInt();
        InstanceType type = InstanceType.values()[typeOrdinal];
        Base<?> instance = null;
        switch (type) {
            case SUB1:
                instance = Sub1.CREATOR.createFromParcel(in);
                break;
            case SUB2:
                instance = Sub2.CREATOR.createFromParcel(in);
                break;
        }
        if (instance != null) {
            list.add(instance);
        }
    }
    return list;
}

方案2:使用反射(无需维护枚举,适合子类较多的场景)

打包逻辑

public void writeBaseList(Parcel dest, List<Base<?>> instancesList) {
    dest.writeInt(instancesList.size());
    for (Base<?> instance : instancesList) {
        // 写入实例的全类名
        dest.writeString(instance.getClass().getName());
        dest.writeParcelable(instance, 0);
    }
}

解包逻辑

public List<Base<?>> readBaseList(Parcel in) {
    List<Base<?>> list = new ArrayList<>();
    int size = in.readInt();
    for (int i = 0; i < size; i++) {
        try {
            // 读取类名并反射获取Class
            String className = in.readString();
            Class<? extends Base<?>> clazz = (Class<? extends Base<?>>) Class.forName(className);
            // 获取子类的CREATOR静态字段
            Creator<? extends Base<?>> creator = clazz.getField("CREATOR").get(null);
            // 创建实例
            Base<?> instance = creator.createFromParcel(in);
            list.add(instance);
        } catch (Exception e) {
            // 处理反射异常(类不存在、CREATOR未定义等)
            e.printStackTrace();
        }
    }
    return list;
}

内容的提问来源于stack exchange,提问作者Bak Itzik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 17:00:58