Android. Можно ли помещать значения @IntDef внутри @interface?

Я пытаюсь реализовать аннотацию @IntDef в разработке Android.

Первый метод: он отлично выглядит с определением, разделенным в классе Constant.java:

public class Constant {
   @IntDef(value={SORT_PRICE, SORT_TIME, SORT_DURATION})
   @Retention(RetentionPolicy.SOURCE)
   public @interface SortType{}
   public static final int SORT_PRICE = 0;
   public static final int SORT_TIME = 1;
   public static final int SORT_DURATION = 2;
}

Использование:

@Constant.SortType int sortType = Constant.SORT_PRICE;

Но все становится намного более грязным, когда в одном файле есть несколько определений (например, UserType, StoreType и т.д.).

Второй метод: Итак, я придумал что-то вроде этого, чтобы отделить значения между определением:

public class Constant {
   @IntDef(value={SortType.SORT_PRICE, SortType.SORT_TIME, SortType.SORT_DURATION})
   @Retention(RetentionPolicy.SOURCE)
   public @interface SortTypeDef{}

   public static class SortType{
       public static final int PRICE = 0;
       public static final int TIME = 1;
       public static final int DURATION = 2;
   }
}

Использование:

@Constant.SortTypeDef int sortType = Constant.SortType.PRICE;

Но, как вы можете видеть, я создал для него два разных имени: SortTypeDef и SortType

Третий метод: Я попытался переместить список возможных значений внутри @interface:

public class Constant {
   @IntDef(value={SortType.SORT_PRICE, SortType.SORT_TIME, SortType.SORT_DURATION})
   @Retention(RetentionPolicy.SOURCE)
   public @interface SortType{
       int PRICE = 0;
       int TIME = 1;
       int DURATION = 2;
   }
}

Использование

@Constant.SortType int sortType = Constant.SortType.PRICE;

Пока он работает, я не знаю, каков недостаток. Можно ли положить возможные значения @IntDef внутри @interface? Существуют ли различия в производительности по трем описанным выше методам?

Ответ 1

Чтобы ваш третий метод работал, вы должны называть values как в интерфейсе. Я использовал ваш код и сделал его работу:

public class Constant {
    @IntDef(value = {SortType.PRICE, SortType.TIME, SortType.DURATION})
    @Retention(RetentionPolicy.SOURCE)
    @interface SortType {
        int PRICE = 0;
        int TIME = 1;
        int DURATION = 2;
    }
}

или

public class Constant {
    @IntDef(value = {SortType.SORT_PRICE, SortType.SORT_TIME, SortType.SORT_DURATION})
    @Retention(RetentionPolicy.SOURCE)
    @interface SortType {
        int SORT_PRICE = 0;
        int SORT_TIME = 1;
        int SORT_DURATION = 2;
    }
}

Использование для второго:

@Constant.SortType int sortType = Constant.SortType.SORT_DURATION;

Выберите один, оба должны работать.

Ответ 2

Я пришел сюда, надеясь найти, почему Android-документы показывают ваш первый метод, но третий метод работает отлично для меня в производственном коде в течение нескольких месяцев. Я не видел причин, чтобы так не поступать. Как вы сказали, он очищает пространство имен, если у вас может быть несколько наборов связанных констант.

Ответ 3

Короткий ответ: для простых проектов это нормально, но для более сложных предпочтительным является первый метод.

Длинный ответ: Хотя байт-код для sortType идентичен во всех трех случаях, есть разница. Ключ заключен в аннотации Retention, которая устанавливает политику хранения на SOURCE. Это означает, что ваша аннотация sortType " должна быть отброшена компилятором", поэтому байт-код для самой аннотации не генерируется.

Первый метод определяет регулярные статические поля вне аннотаций с созданным для них обычным байткодом. Второй и третий случаи определяют константы в аннотациях, а байт-код для констант не генерируется.

Если у компилятора есть доступ к исходному файлу, содержащему ваше объявление sortType, любой метод прекрасен, а байт-код для sortType идентичен. Но если исходный код недоступен (например, вы только скомпилировали библиотеку), аннотация недоступна. Для первого подхода только сама аннотация не доступна, но для последних значения констант также недоступны.

Я предпочитал третий метод как самый чистый и структурированный. Раньше я сталкивался с проблемой: когда я начал писать тесты Espresso для этого кода, компилятор не имел доступа к исходному коду, определяющему аннотацию. Мне пришлось либо перейти к каноническому объявлению IntDef, либо использовать целые значения вместо символических констант для теста.

Итак, нижняя строка:

  • придерживаться канонического пути, если ваша аннотация не является внутренней для вашего кода, и вы не ссылаетесь на нее нигде, включая тесты

Ответ 4

Кажется, это хорошее место для emum...

public enum SortEnum {    СРОК,    ЦЕНА,    ВРЕМЯ; }