【问题标题】:Abstract class as parcelable抽象类作为parcelable
【发布时间】:2014-04-29 21:07:04
【问题描述】:

我的应用中基本上有以下结构:

如果没有抽象类ProjectItem,实现这样的结构会很简单,但在这种情况下,我不知道如何实现。

抽象类ProjectItem 需要一个CREATOR,因为它应该是可包裹的。 (喜欢 in.readTypedList(mProjectItems, ProjectItem.CREATOR);在构造函数Project(Parcel in)内)

但实际上,出于逻辑原因,CREATOR 只能在其派生类中实现。

那么,如何实现这个结构以保持类Project 可包裹??

编辑

这是Project 的构造函数之一的样子:

private Project(Parcel in) {
    in.readTypedList(mProjectItems, ProjectItem.CREATOR);
}

但正如我已经说过的,ProjectItem 不应该实现 CREATOR

【问题讨论】:

    标签: java android design-patterns inheritance parcelable


    【解决方案1】:

    我的解决方案类似于 evertvandenbruel 的解决方案。但我使用 int 识别具体类,以便我可以使用 switch 块。我在静态 getConcreteClass(Parcel) 方法中也有那个开关块。

    AbstractClass.java

    public abstract class AbstractClass implements Parcelable {
    
    public static final int CLASS_TYPE_ONE = 1;
    public static final int CLASS_TYPE_TWO = 2;
    
    public static final Creator<AbstractClass> CREATOR = new Creator<AbstractClass>() {
        @Override
        public AbstractClass createFromParcel(Parcel source) {
    
            return AbstractClass.getConcreteClass(source);
        }
    
        @Override
        public AbstractClass[] newArray(int size) {
            return new AbstractClass[size];
        }
    };
    
    protected String mAbstractClassString;
    
    public AbstractClass(String abstractClassString) {
        mAbstractClassString = abstractClassString;
    }
    
    public AbstractClass(Parcel source) {
        mAbstractClassString = source.readString();
    }
    
    public static AbstractClass getConcreteClass(Parcel source) {
    
        switch (source.readInt()) {
            case CLASS_TYPE_ONE:
                return new ConcreteClassOne(source);
            case CLASS_TYPE_TWO:
                return new ConcreteClassTwo(source);
            default:
                return null;
        }
    }
    
    @Override
    public int describeContents() {
        return 0;
    }
    
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(mAbstractClassString);
    }
    
    @Override
    public String toString() {
        return "Parent String: " + mAbstractClassString + '\n';
    }
    }
    

    ConcreteClassOne.java

    public class ConcreteClassOne extends AbstractClass {
    
    private String mString;
    
    public ConcreteClassOne(String abstractClassMemberString, String string) {
        super(abstractClassMemberString);
    
        mString = string;
    }
    
    public ConcreteClassOne(Parcel source) {
        super(source);
        mString = source.readString();
    }
    
    @Override
    public void writeToParcel(Parcel dest, int flags) {
    
        dest.writeInt(CLASS_TYPE_ONE);
        super.writeToParcel(dest, flags);
        dest.writeString(mString);
    }
    
    @Override
    public String toString() {
        return super.toString().concat("Child String: " + mString);
    }
    }
    

    ConcreteClassTwo.java

    public class ConcreteClassTwo extends AbstractClass {
    
    private String mString;
    private int mInt;
    
    public ConcreteClassTwo(String abstractClassString, String string, int anInt) {
        super(abstractClassString);
        mString = string;
        mInt = anInt;
    }
    
    public ConcreteClassTwo(Parcel source) {
        super(source);
        mString = source.readString();
        mInt = source.readInt();
    }
    
    @Override
    public void writeToParcel(Parcel dest, int flags) {
    
        dest.writeInt(CLASS_TYPE_TWO);
        super.writeToParcel(dest, flags);
        dest.writeString(mString);
        dest.writeInt(mInt);
    }
    
    @Override
    public String toString() {
    
        String string = super.toString();
        for (int i = 0; i < mInt; i++) {
            string = string.concat("Child String: " + mString + '\n');
        }
        return string;
    }
    }
    

    【讨论】:

    • 请注意:从 Java 7 开始,switch 语句现在支持字符串类型。对于 Java 7 之前的用户来说,使用 int 来提高可读性的优化一点也不差。
    【解决方案2】:

    所选答案(来自 evertvandenbruel 的帖子)有一个错误。当只有一个子类被打包时,正确的代码必须考虑到打包,而不仅仅是超类对象的列表。

    所有其他代码都应该相同,关键是您必须在所有创建者中读取类型变量(参见下面的代码)。否则在尝试解包子类对象时会出现排序问题

    例如:

    package com.example.parcelable_example.model;
    
    import android.os.Parcel;
    import android.os.Parcelable;
    
    public class Cat extends Animal{
    
        public Cat(String name){
            super(name, "Cat");
        }
    
        public int describeContents() {
            return 0;
        }
    
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(getType());
            super.writeToParcel(dest, flags);
        }
    
        public Cat(Parcel source) {
            super(source);      
        }
    
        public static final Parcelable.Creator<Cat> CREATOR = new Parcelable.Creator<Cat>() {
            public Cat createFromParcel(Parcel in) {
                /** DO NOT FORGET THIS!!! **/
                type = in.readString();
                return new Cat(in);
            }
    
            public Cat[] newArray(int size) {
                return new Cat[size];
            }
        };
    
    }
    

    【讨论】:

    • 为什么不把那行放在 Animal 的 Parcel 构造函数中?
    • 对于这种情况:我需要将 Cat、Dog 等(Animal 的子类)列表打包为类型为“Animal”的数组列表。在这种情况下,Animal 的 CREATOR 将与 writeToParcel 中的数据一起使用,in.readString() 用于确定类型,以便可以使用其 parcel 构造函数实例化正确的子类,该构造函数应使用 super 调用 Animal 的 parcel 构造函数(在)。如果您在 Animal 构造函数中读取类型,那么您将得到一个损坏的实现——为了创建 Dog 对象而不是 Cat 对象,已经读取了该类型。
    • 为什么?您调用 new Cat(in) 调用 super(source) 比 Animal 的构造函数读取类型。为什么要调用 Dogs 构造函数?:编辑我明白了:使用 Animal CREATOR 时它会被读取两次,然后包裹就被破坏了。但是你在这里做什么是不可能的,因为你设置的类型是什么?
    • 我通过向名为readObjectType 的parcel 构造函数添加一个布尔参数来出售它,这在使用抽象CREATOR 时为false,而在具体CREATOR 中为true。
    • 再来一次?除了提供的源之外,您不需要其他任何东西。不需要布尔参数。无论使用哪个 CREATOR,您总是会读取对象类型。关键是在 parcelable 实现中只会使用一个 CREATOR,但它们仍然必须读取类型(因为抽象超类的 writeToParcel() 实现,它们都写入类型)。
    【解决方案3】:

    这个问题源于一个错误的假设。


    这是原帖的引述。

    抽象类 ProjectItem 需要一个 CREATOR,因为它应该是 可打包。

    其实,超类没有必要定义CREATOR,因为它是抽象的。


    这是一个演示该方法的最小示例。

    /*   Super class   */
    
    abstract class SuperClass
            implements Parcelable {
    
        protected SuperClass(Parcel in) {
            mSuperId = in.readLong();
        }
    
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeLong(mSuperId);
        }
    
    }
    
    
    
    /*   Sub class   */
    
    public class SubClass
            extends SuperClass {
    
        protected SubClass(Parcel in) {
            super(in);
            mSubId = in.readLong();
        }
    
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            super.writeToParcel(dest, flags);
            dest.writeLong(mSubId);
        }
    
        @Override
        public int describeContents() {
            return 0;
        }
    
        public static final Creator<SubClass> CREATOR = new Creator<SubClass>() {
    
            @Override
            public SubClass createFromParcel(Parcel in) {
                return new SubClass(in);
            }
    
            @Override
            public SubClass[] newArray(int size) {
                return new SubClass[size];
            }
    
        };
    
    }
    
    
    
    /*   Usage   */
    
    class AnotherClass {
    
        void aMethod() {
            Bundle args = new Bundle();
            args.putParcelable("EXTRA_SUPER_CLASS", subClassObject);
        }
    
    }
    

    【讨论】:

    • 在某些情况下,您需要父类来实现 CREATOR,例如在 AIDL 中传递一个抽象类
    【解决方案4】:
    public abstract class A implements Parcelable {
        private int a;
    
        protected A(int a) {
            this.a = a;
        }
    
        public void writeToParcel(Parcel out, int flags) {
            out.writeInt(a);
        }
    
        protected A(Parcel in) {
            a = in.readInt();
        }
    }
    
    public class B extends A {
        private int b;
    
        public B(int a, int b) {
            super(a);
            this.b = b;
        }
    
        public static final Parcelable.Creator<B> CREATOR = new Parcelable.Creator<B>() {
            public B createFromParcel(Parcel in) {
                return new B(in);
            }
    
            public B[] newArray(int size) {
                return new B[size];
            }
        };
    
        public int describeContents() {
            return 0;
        }
    }
    

    【讨论】:

    • 不幸的是,这并没有太大帮助,因为我当前的结构需要在父类中实现CREATOR
    猜你喜欢
    • 2023-04-06
    • 1970-01-01
    • 1970-01-01
    • 2020-10-16
    • 1970-01-01
    • 2014-08-27
    • 2017-05-05
    • 2010-11-18
    • 2016-02-01
    相关资源
    最近更新 更多