Android進階之將註解@IntDef @StringDef替代列舉類(enum)
阿新 • • 發佈:2019-01-01
1 概述
Enum是java中一種包含固定常數的型別。當我們需要預先定義一些值,並限定範圍時,使用 Enum,來做到編寫和編譯都查錯。
Java的Enum的實質是特殊單例的靜態成員變數,可以在編寫器,編譯器做到各種靜態檢查防呆;在執行期,所有列舉類作為單例,全部載入到記憶體中。
因此,Enum增加了APK的記憶體佔用,比常量多5到10倍的記憶體佔用。所以放棄列舉,就是關於安卓應用效能的記憶體佔用部分的最佳實踐方法之一。
2 為什麼要使用列舉
2.1 例子
public class SexTest { private final int MAN = 101; private final int WOMEN = 102; private int sex; //設定性別 private void setSex(int sex) { this.sex = sex; } // 獲取性別 private String getSex() { if (MAN == sex) { return "男"; } else if (WOMEN == sex) { return "女"; } else { return "未知"; } } public void main() { // 設定為101入參,而非限定的MAN setSex(101); String sex = getSex(); System.out.println("sex: " + sex); // 輸出:sex: 男 // 設定為102入參 setSex(102); String resultSex = getSex(); System.out.println("resultSex: " + resultSex); // 輸出:resultSex: 未知 } }
2.2 缺點
當我們定義了一個男女的final整型作為入參時,不一定保證入參的都是我們想要的入參,這裡就有一個 型別不安全的問題出現,而列舉就可以解決這個問題。
3 列舉類
3.1 例子
public class SexEnumTest { public enum Sex { MAN, WOMEN } private Sex sex; // 設定性別 private void setSex(Sex sex) { this.sex = sex; } // 獲取性別 private String getSex() { if (MAN == sex) { return "男"; } else if (WOMEN == sex) { return "女"; } else { return "未知"; } } public void main() { // 這裡的入參必須為Sex列舉類中的其中一個列舉常量 // 絕對不允許輸入沒有再Sex列舉裡面定義的常量 setSex(Sex.MAN); // 錯誤 setSex(101); String resultSex = getSex(); System.out.println("resultSex: " + resultSex); //out:resultSex: 男 } }
3.2 優點
(1)利用列舉,在setSex()方法裡面對入參做了列舉Sex的限制,如圖:
(2)對於想輸入任何非列舉類Sex裡面定義的列舉常量,編譯都是不能通過的;
(3)這就很好的限制了入參混亂的問題。
3.3 缺點
(1)每一個列舉值都是一個單例物件,在使用它時會增加額外的記憶體消耗,所以列舉相比與Integer和String會佔用更多的記憶體;
(2)較多的使用Enum會增加DEX檔案的大小,會造成執行時更多的IO開銷,使我們的應用需要更多的空間。特別是分dex多的大型APP,列舉的初始化很容易導致ANR。
4 不使用列舉型別,使用@IntDef/@StringDef + @interface的解決方案
既然是因為引數的型別太泛了造成的型別不安全,那麼我只要將引數限定在某一個型別集合裡面,用@IntDef/@StringDef + @interface來進行限定引數。
4.1 例子
public class SexIntDef {
// 表示開啟Doc文件
@Documented
@IntDef({SEX.UNKNOWN, SEX.MALE, SEX.FEMALE})
// 表示註解作用範圍,引數註解,成員註解,方法註解
@Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD})
// 表示註解屬於(編譯)級別,僅存在於原始碼中,在class位元組碼檔案中不包含。
@Retention(RetentionPolicy.SOURCE)
// 介面,定義新的註解型別
public @interface SEX {
int UNKNOWN = 0; // 未知(保密)
int MALE = 1; // 男
int FEMALE = 2; // 女
}
private @SEX int sex = SEX.MALE;
private @SEX int sex1 = 101; // 錯誤
private void setSex(@SEX int sex){
this.sex = sex;
}
public void main() {
// 絕對不允許輸入沒有在@IntDef/@StringDef + @interface限定的引數
setSex(SEX.MALE);
// 錯誤
setSex(1);
}
}
4.2 優點
(1)如果我們嘗試在呼叫setSex()方法的時候,傳入不在限定之內的值,那麼編譯就不會通過,有錯誤提示,如圖:
4.3 同理,我們也可以使用@StringDef
4.4 常用模式
// 性別
@IntDef({SEX.UNKNOWN, SEX.MALE, SEX.FEMALE})
@Retention(RetentionPolicy.SOURCE)
public @interface SEX {
int UNKNOWN = 0; // 未知(保密)
int MALE = 1; // 男
int FEMALE = 2; // 女
}