Android - 多态和Parcelable [英] Android - polymorphism and 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屋!