Android - 多态和Parcelable [英] Android - polymorphism and Parcelable

查看:130
本文介绍了Android - 多态和Parcelable的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在从包裹中书写/阅读时,我无法弄清楚如何使用多态性。
我明白我需要在基类中实现Parcelable,并且还需要在所有派生类中实现(在子类具有我想要写入parcel的附加属性的情况下)。
我不明白和我不知道甚至可能的是 - 如何从Parcelable读取子类,即,我怎么知道我从包裹中读取什么类的子类。
我可以做一些破解,就像在包裹中写一些指示符来告诉我使用什么类加载器,但我认为会有一些更优雅的方式,否则没有多少使用多态。

I can't figure out how to use polymorohism when writing/reading from parcel. I understand that I need to implement Parcelable in base class, and also in all derived classes (in the case that subclasses have additional properties that I would want to write into parcel). What I don't understand and what I don't know if it's even possible is - how to read subclass from Parcelable i.e., how do I know what kind of subclass I am reading from parcel. I can do some hack like writing some indicator into parcel that would tell me what class loader to use, but I thought there would be some more elegant way, otherwise there is no much use of polymorphism.

为了说明我的问题,假设我有这样的课程:

To illustrate my question, let say I have classes like this:

Shape.class

Shape.class

public class Shape implements Parcelable {
public float area;

public Shape() {
}

public Shape(Parcel in) {
    area = in.readFloat();
}

public void writeToParcel(Parcel dest, int flags) {
    dest.writeFloat(area);
    }
//CREATOR etc..
}

RectangleShape .class

RectangleShape.class

 public class RectangleShape extends Shape {

    float a;
    float b;

    public RectangleShape(Parcel in) {
        super(in);
        a = in.readFloat();
        b = in.readFloat();
    }

    public void writeToParcel(Parcel dest, int flags) {
        dest.writeFloat(area);
        dest.writeFloat(a);
        dest.writeFloat(b);
    }

//CREATOR etc..
}

CircleShape.class

CircleShape.class

public static class CircleShape extends Shape {
        float r;

    public CircleShape(Parcel in) {
        super(in);
        r = in.readFloat();
    }

    public void writeToParcel(Parcel dest, int flags) {
        dest.writeFloat(area);
        dest.writeFloat(r);
    }
//CREATOR etc..
 }

现在,在其他一些课程中我有这样的东西:

Now, in some other class I have something like this:

 public  class Geometry implements Parcelable {

    Shape myShape; 

    public Geometry (boolean condition) {

        if (condition) 
            myShape = new CircleShape(4.5);
        else
            myShape = new RectangleShape(3.4, 3.5);
    }


    @Override
    public Geometry(Parcel in) {
        in.readParcelable(??? - **how do I know what class loader to put here?** )
     }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeParcelable(myShape,flags);
    }
//CREATOR etc..
}

是有什么办法,我可以告诉android写入包时它是什么子类,没有隐式检查实例类型?

Is there any way that i can "tell" android when writing into parcel what subclass it is, without implicit check of instance type?

推荐答案

我写了这个小片段并将其加载到 FrameLayout 中要检查的主要活动 - 我得到了积极的结果(来自包裹的重建对象是 DerivedClass 类型,尽管该对象的引用都是 ObjectClass )。

I wrote this little fragment and loaded it into a FrameLayout in the main activity to check - and I got positive results (the rebuilt object from the parcel was of type DerivedClass although the references of the object were all of type ObjectClass).

import android.app.Fragment;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class Fragment2 extends Fragment {

    public ObjectClass obj;

    public static final String OBJECT_KEY = "Object key";

    public static Fragment2 newInstance(){
        Fragment2 fragment = new Fragment2();
        Bundle args = new Bundle();
        ObjectClass obj = new DerivedClass(); //a DerivedClass object
                               //referenced by an ObjectClass reference
        args.putParcelable(OBJECT_KEY, obj);
        fragment.setArguments(args);
        return fragment;
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_main2, container, false);
        if (savedInstanceState == null){
            Bundle args = getArguments();
            obj = args.getParcelable(OBJECT_KEY); //without supplying the ClassLoader
            ((TextView)view.findViewById(R.id.section_label)).setText(obj.getTitle()); 
                     //The text that is displayed is: "Child class"!
        }
        return view;
    }

    //The parent class
    public static class ObjectClass implements Parcelable {
        String title = "Parent class";

        public String getTitle(){
            return title;
        }

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

        @Override
        public void writeToParcel(Parcel dest, int flags) {

        }

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

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

    //The child class
    public static class DerivedClass extends ObjectClass {
        String title2 = "Child class";

        public String getTitle() {
            return title2;
        }

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

        @Override
        public void writeToParcel(Parcel dest, int flags) {

        }

        public static final Parcelable.Creator<DerivedClass> CREATOR = new Parcelable.Creator<DerivedClass>() {
            @Override
            public DerivedClass createFromParcel(Parcel source) {
                return new DerivedClass();
            }

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

在主要活动中, onCreate(Bundle)方法:

getFragmentManager().beginTransaction().replace(R.id.frameContainer, Fragment2.newInstance()).commit();

我的想法导致我检查:因为android必须弄明白从包裹中重新创建对象时要使用的类(要使用 CREATOR ),我认为必须将此附加信息与指定信息一起存储(在<$中指定)将它放入捆绑包时c $ c> writeToParcel(Parcel,int)方法)。为了确定这个类我认为它可以使用引用的类型或使用对象本身(例如使用 getClass())。如果它使用对象类型本身,那当然意味着多态性是可能的。从上面的检查看起来确实如此。

My thoughts that led me to this check: As android must figure out what class to use when recreating the object from the parcel (which CREATOR to use), I figured it must store this additional information together with the specified information ( specified in the writeToParcel(Parcel, int) method) when putting it into the bundle. To determine this class I thought it can either use the reference's type or use the object itself (e.g. using getClass() on it). If it uses the object type itself that of course means polymorphism is possible. From my check above it looks like that is indeed the case.

结论并猜测你的例子:
我认为例如,尽量不要显式传递ClassLoader。传递 null 。来自参数的文档包#readParcelable(ClassLoader)


ClassLoader :一个ClassLoader,用于实例化Parcelable对象,或者为默认的类加载器为null。

ClassLoader: A ClassLoader from which to instantiate the Parcelable object, or null for the default class loader.

我没试过你的配置,所以我不确定它是否会像我的例子一样工作。具体来说,我不确定 Parcel#readParcelable(ClassLoader = null)实际上是否与 Bundle#getParcelable(String)。但如果我的理由是正确的 - 它应该(我假设 Bundle#getParcelable(String)使用默认的ClassLoader,只要 Bundle #setClassLoader(ClassLoader) 未被调用。)

I didn't try it with your configuration, so I'm not sure it will work in the same way as in my example. Specifically, I'm not sure whether Parcel#readParcelable(ClassLoader=null) actually works in the same manner as Bundle#getParcelable(String). but if my rationale is right - it should (I assume Bundle#getParcelable(String) uses the default ClassLoader as long as Bundle#setClassLoader(ClassLoader) wasn't called).

如果有效,请发表评论并告诉我。

Please comment and let me know if it worked for you.

这篇关于Android - 多态和Parcelable的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆