12/08/2018, 14:28

Android - Thay thế Enums bằng Enumerated Annotations

TẠI SAO PHẢI THAY THẾ ENUM? Android Team khuyến cáo rằng bạn nên tránh sử dụng enums bất cứ khi nào bạn có thể. Enums rất tiện lợi trong Java nhưng không may mắn nó có thể là nỗi đau khi mà kích thước và tốc độ là cần thiết. Ví dụ public enum Shrubbery { GROUND , CRAWLING , HANGING } ...

TẠI SAO PHẢI THAY THẾ ENUM?

Android Team khuyến cáo rằng bạn nên tránh sử dụng enums bất cứ khi nào bạn có thể. Enums rất tiện lợi trong Java nhưng không may mắn nó có thể là nỗi đau khi mà kích thước và tốc độ là cần thiết. Ví dụ

public enum Shrubbery { GROUND, CRAWLING, HANGING }

enum trên thêm 740 bytes vào file .dex khi so sánh với class sử dụng 3 public static final int. Trong lần sử dụng đầu tiên, class intializer invokes phương thức trên các đối tượng thẻ hiện của mỗi giá trị. Mỗi đối tượng sở hữu static field, và tập giá trị được lưu trong một mảng (một static field được gọi là “$VALUES”). Blah blah. Rất nhiều code và data hơn là chỉ có 3 số nguyên. Hơn nữa khi dùng

Shrubbery shrub = Shrubbery.GROUND;

Sẽ là static field lookup. Nhưng nếu “GROUND” là một static final int thì trình biên dịch sẽ dùng nó như là một hằng số và thay thế inline.

Đoạn này hơi khó dịch nhưng bạn có thể tham khảo câu trả lời ở đây

OK, lý do đã rõ rồi và thay thế thì thay thế như thế nào? Trước đây tôi có một enum Mode{Single,Multi} và method setMode(Mode mode). Chạy rất ổn rồi. Giờ muốn thay thế enum đó tôi sẽ sử dụng int đổi method thành setMode(int mode) nhưng vấn đề là chỉ có 2 mode nhưng lại có thể truyền số int bất kì nào vào method đó dẫn đến sai.

Giải pháp đó chính là sử dụng Enumerated Annotations. Nó có thể thêm vào kiểu trả về hoặc method parameter, từ đó tools của bạn có thể biết được giá trị nào là chấp nhận được. Mục đích chính là đảm bảo type safety cho method parameter hoặc return type khi viết code.

Enumerated Annotations thường được sử dụng để thay thế enums. Google khuyến khích tránh sử dụng enum bởi vì enums yêu cầu bộ nhớ gấp đôi so với static constants.

IntDef

IntDef là một cách để thay thế integer enum nơi mà tham số chỉ chấp nhận các giá trị int nào đó. Ví dụ chúng ta muốn kiểu của một feed item như sau:

public class ItemTypeDescriptor {
  public static final int TYPE_MUSIC = 0;
  public static final int TYPE_PHOTO = 1;
  public static final int TYPE_TEXT = 2;

  public final int itemType;

  public ItemTypeDescriptor(int itemType) {
    this.itemType = itemType;
  }
}

Bây giờ không có gì đảm bảo rằng itemType được truyền vào hàm tạo là hợp lệ. Chúng ta cần sử dụng IntDef để đảm bảo rằng giá trị truyền vào là một trong những giá trị mong muốn bằng cách thêm các annotations:

public class ItemTypeDescriptor {
  // ... type definitions
  // Describes when the annotation will be discarded
  @Retention(RetentionPolicy.SOURCE)
  // Enumerate valid values for this interface
  @IntDef({ TYPE_MUSIC, TYPE_PHOTO, TYPE_TEXT })
  // Create an interface for validating int types
  public @interface ItemTypeDef { }
  // Mark the argument as restricted to these enumerated types
  public ItemTypeDescriptor(@ItemTypeDef int itemType) {
    this.itemType = itemType;
  }
}

Giờ nếu bạn truyền một giá trị không hợp lệ vào hàm tạo, Android Studio sẽ báo lỗi.

StringDef

StringDef tương tự như IntDef, là một cách thay thế String enum.

public class FilterColorDescriptor {
  public static final String FILTER_BLUE = "blue";
  public static final String FILTER_RED = "red";
  public static final String FILTER_GRAY = "gray";

  public final String filterColor;

  public FilterColorDescriptor(String filterColor) {
    this.filterColor = filterColor;
  }
}

Cũng không có gì đảm bảo giá trị filterColor truyền vào là hợp lệ cho nên phải sử dụng StringDef để đảm bảo giá trị truyền vào là một trong các giá trị được định nghĩa phía trên.

public class FilterColorDescriptor {
  // ... type definitions
  // Describes when the annotation will be discarded
  @Retention(RetentionPolicy.SOURCE)
  // Enumerate valid values for this interface
  @StringDef({ FILTER_BLUE, FILTER_RED, FILTER_GRAY })
  // Create an interface for validating int types
  public @interface FilterColorDef { }
  // Mark the argument as restricted to these enumerated types
  public FilterColorDescriptor(@FilterColorDef String filterColor) {
    this.filterColor = filterColor;
  }
}

Nhờ đó rõ ràng là method chấp nhập giá trị String như sau

Screen Shot 2016-12-28 at 1.41.39 PM.png

Nhưng truyền sai giá trị sẽ báo lỗi như sau:

Screen Shot 2016-12-28 at 1.42.08 PM.png

Tham khảo tại đây

HẾT

0