實體類實現(xiàn)Parcelable
接口后枚舉字段該怎么寫?正好碰到這個問題,記錄一下。
枚舉類
public enum EnumBean {
STEAM,
EPIC
}
枚舉字段序列化,這里直接上寫法。實際上用了AS插件Android Parcelable Code Generator
生成,當然還是本著知其所以然的態(tài)度,思考一下為啥是這個寫法。
public class Test implements Parcelable {
public String name;
public int age;
public EnumBean e;
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.name);
dest.writeInt(this.age);
dest.writeInt(this.e == null ? -1 : this.e.ordinal());
}
public void readFromParcel(Parcel source) {
this.name = source.readString();
this.age = source.readInt();
int tmpE = source.readInt();
this.e = tmpE == -1 ? null : EnumBean.values()[tmpE];
}
public Test() {
}
protected Test(Parcel in) {
this.name = in.readString();
this.age = in.readInt();
int tmpE = in.readInt();
this.e = tmpE == -1 ? null : EnumBean.values()[tmpE];
}
public static final Parcelable.Creator<Test> CREATOR = new Parcelable.Creator<Test>() {
@Override
public Test createFromParcel(Parcel source) {
return new Test(source);
}
@Override
public Test[] newArray(int size) {
return new Test[size];
}
};
}
其實從write
和read
字段e就可以看出,這里寫入的是枚舉類數(shù)組下標,讀取時根據(jù)下標取出枚舉類實體。
dest.writeInt(this.e == null ? -1 : this.e.ordinal());
this.e = tmpE == -1 ? null : EnumBean.values()[tmpE];
眾所周知枚舉中的每個聲明都會創(chuàng)建枚舉類對象,ordinal()
也就是枚舉中聲明的順序,對應到枚舉類數(shù)組values()
的下標。
看一下編譯后的EnumBean.class
文件字節(jié)碼
//枚舉類默認繼承Enum
public final enum com/chenxuan/kotlin/EnumBean extends java/lang/Enum
public final static enum Lcom/chenxuan/kotlin/EnumBean; STEAM
public final static enum Lcom/chenxuan/kotlin/EnumBean; EPIC
private final static synthetic [Lcom/chenxuan/kotlin/EnumBean; $VALUES
//構造方法
private <init>(Ljava/lang/String;I)V
L0
LINENUMBER 3 L0
ALOAD 0
ALOAD 1
ILOAD 2
INVOKESPECIAL java/lang/Enum.<init> (Ljava/lang/String;I)V
RETURN
//static塊,創(chuàng)建聲明的枚舉類對象,枚舉類數(shù)組。
static <clinit>()V
L0
LINENUMBER 4 L0
NEW com/chenxuan/kotlin/EnumBean
DUP
LDC "STEAM"
ICONST_0
INVOKESPECIAL com/chenxuan/kotlin/EnumBean.<init> (Ljava/lang/String;I)V
PUTSTATIC com/chenxuan/kotlin/EnumBean.STEAM : Lcom/chenxuan/kotlin/EnumBean;
L1
LINENUMBER 5 L1
NEW com/chenxuan/kotlin/EnumBean
DUP
LDC "EPIC"
ICONST_1
INVOKESPECIAL com/chenxuan/kotlin/EnumBean.<init> (Ljava/lang/String;I)V
PUTSTATIC com/chenxuan/kotlin/EnumBean.EPIC : Lcom/chenxuan/kotlin/EnumBean;
L2
LINENUMBER 3 L2
ICONST_2
ANEWARRAY com/chenxuan/kotlin/EnumBean
DUP
ICONST_0
GETSTATIC com/chenxuan/kotlin/EnumBean.STEAM : Lcom/chenxuan/kotlin/EnumBean;
AASTORE
DUP
ICONST_1
GETSTATIC com/chenxuan/kotlin/EnumBean.EPIC : Lcom/chenxuan/kotlin/EnumBean;
AASTORE
PUTSTATIC com/chenxuan/kotlin/EnumBean.$VALUES : [Lcom/chenxuan/kotlin/EnumBean;
RETURN
public static values()[Lcom/chenxuan/kotlin/EnumBean;
L0
LINENUMBER 3 L0
GETSTATIC com/chenxuan/kotlin/EnumBean.$VALUES : [Lcom/chenxuan/kotlin/EnumBean;
INVOKEVIRTUAL [Lcom/chenxuan/kotlin/EnumBean;.clone ()Ljava/lang/Object;
CHECKCAST [Lcom/chenxuan/kotlin/EnumBean;
ARETURN
public static valueOf(Ljava/lang/String;)Lcom/chenxuan/kotlin/EnumBean;
L0
LINENUMBER 3 L0
LDC Lcom/chenxuan/kotlin/EnumBean;.class
ALOAD 0
INVOKESTATIC java/lang/Enum.valueOf (Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
CHECKCAST com/chenxuan/kotlin/EnumBean
ARETURN
很好理解,聲明了兩個EnumBean類型的變量STEAM和EPIC并在static塊中初始化;同時聲明了EnumBean數(shù)組$VALUES,按照枚舉聲明順序添加到數(shù)組。
需注意枚舉變量初始化時調用的構造方法傳入了兩個參數(shù):聲明的枚舉變量對應的字符串STEAM、下標0。
LDC "STEAM"
ICONST_0
INVOKESPECIAL com/chenxuan/kotlin/EnumBean.<init> (Ljava/lang/String;I)V
EnumBean構造方法調用父類Enum的構造方法
private <init>(Ljava/lang/String;I)V
L0
LINENUMBER 3 L0
ALOAD 0
ALOAD 1
ILOAD 2
INVOKESPECIAL java/lang/Enum.<init> (Ljava/lang/String;I)V
RETURN
繼續(xù)看Enum的構造方法
private final String name;
private final int ordinal;
protected Enum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
public final int ordinal() {
return ordinal;
}
如此一來,ordinal
就和枚舉聲明順序或者說枚舉數(shù)組下標聯(lián)系起來了。
回看readFromParcel
中EnumBean.values()[tmpE]
,由上述字節(jié)碼可知values()
方法調用$VALUES.clone()
也就是枚舉類數(shù)組,然后根據(jù)數(shù)組下標取出枚舉類實體。
還有我們常用的valueOf()
方法,調用父類Enum.valueOf()
傳入了EnumBean.class
。
public static valueOf(Ljava/lang/String;)Lcom/chenxuan/kotlin/EnumBean;
L0
LINENUMBER 3 L0
LDC Lcom/chenxuan/kotlin/EnumBean;.class
ALOAD 0
INVOKESTATIC java/lang/Enum.valueOf (Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
CHECKCAST com/chenxuan/kotlin/EnumBean
ARETURN
Enum.valueOf()
public static <T extends Enum<T>> T valueOf(Class<T> enumType,
String name) {
Objects.requireNonNull(enumType, "enumType == null");
Objects.requireNonNull(enumType, "name == null");
T[] values = getSharedConstants(enumType);
if (values == null) {
throw new IllegalArgumentException(enumType.toString() + " is not an enum type.");
}
for (int i = values.length - 1; i >= 0; --i) {
T value = values[i];
if (name.equals(value.name())) {
return value;
}
}
throw new IllegalArgumentException(
"No enum constant " + enumType.getCanonicalName() + "." + name);
}
Enum.getSharedConstants()
根據(jù)傳入的class獲取枚舉類數(shù)組values
public static <T extends Enum<T>> T[] getSharedConstants(Class<T> enumType) {
return (T[]) sharedConstantsCache.get(enumType);
}
private static final BasicLruCache<Class<? extends Enum>, Object[]> sharedConstantsCache
= new BasicLruCache<Class<? extends Enum>, Object[]>(64) {
@Override protected Object[] create(Class<? extends Enum> enumType) {
return enumValues(enumType);
}
};
private static Object[] enumValues(Class<? extends Enum> clazz) {
if (!clazz.isEnum()) {
return null;
}
try {
Method valueMethod = clazz.getDeclaredMethod("values");
return (Object[]) valueMethod.invoke(null);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
這里通過反射獲取到枚舉類數(shù)組,然后迭代數(shù)組找到name相同的枚舉類。而name其實就是聲明的枚舉變量對應的字符串,在static塊中初始化時調用到父類Enum構造方法賦值。
NEW com/chenxuan/kotlin/EnumBean
DUP
LDC "STEAM"
ICONST_0
INVOKESPECIAL com/chenxuan/kotlin/EnumBean.<init> (Ljava/lang/String;I)V
PUTSTATIC com/chenxuan/kotlin/EnumBean.STEAM : Lcom/chenxuan/kotlin/EnumBean;