Android – Est-il correct de mettre les valeurs @IntDef dans @interface?

J’essaie d’implémenter l’annotation @IntDef dans le développement Android.

Première méthode : elle a fière allure avec la définition séparée dans une classe 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; } 

Usage:

 @Constant.SortType int sortType = Constant.SORT_PRICE; 

Mais les choses deviennent beaucoup plus compliquées quand il y a plusieurs définitions (par exemple, UserType, StoreType, etc.) dans un seul fichier.

Deuxième méthode: J’ai donc trouvé quelque chose comme ça pour séparer les valeurs entre les définitions:

 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; } } 

Usage:

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

Mais comme vous pouvez le voir, j’ai créé deux noms différents: SortTypeDef et SortType

Troisième méthode: j’ai essayé de déplacer la liste des valeurs possibles dans @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; } } 

Usage

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

Bien que cela fonctionne, je ne sais pas quel est l’inconvénient. Est-il correct de mettre les valeurs possibles de @IntDef dans @IntDef ? Y a-t-il des différences de performance entre les trois méthodes ci-dessus?

Réponse courte : pour les projets simples, c’est correct, mais pour les plus complexes, la première méthode est préférable.

Réponse longue : Bien que bytecode pour sortType soit identique dans les trois cas, il existe une différence. La clé réside dans l’annotation de Retention , qui définit la stratégie de rétention sur SOURCE . Cela signifie que votre annotation SortType doit être “supprimée par le compilateur “, donc le bytecode pour les annotations n’est pas généré.

La première méthode définit des champs statiques réguliers en dehors des annotations, avec le bytecode régulier généré pour eux. Les deuxième et troisième cas définissent des constantes dans les annotations et le bytecode des constantes n’est pas généré.

Si le compilateur a access au fichier source contenant votre déclaration SortType , les deux méthodes sont sortType et bytecode pour sortType est identique. Mais si le code source n’est pas accessible (par exemple, vous n’avez compilé que la bibliothèque), l’annotation n’est pas accessible. Pour la première approche, seule l’annotation elle-même n’est pas accessible, mais pour les dernières, les valeurs des constantes ne sont pas accessibles.

Je préférais la troisième méthode comme étant la plus propre et structurée. J’ai eu jusqu’à un jour où j’ai rencontré un problème: lorsque j’ai commencé à écrire des tests Espresso pour ce code, le compilateur n’avait pas access au code source définissant l’annotation. J’ai dû soit basculer vers la déclaration IntDef canonique, soit utiliser des valeurs entières au lieu de constantes symboliques pour le test.

Donc, le résultat est le suivant:

  • s’en tenir à la manière canonique à moins que votre annotation ne soit interne à votre code et que vous ne vous y référiez nulle part, y compris les tests

Pour que votre troisième méthode fonctionne, vous devez nommer des values comme dans l’interface. J’ai utilisé votre code et fait fonctionner:

 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; } } 

Ou

 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; } } 

Utilisation pour la seconde:

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

Choisissez-en un, les deux devraient fonctionner.

Je suis venu ici en espérant découvrir pourquoi les documents Android montrent votre première méthode, mais la troisième méthode fonctionne bien pour moi en code de production depuis des mois. Je n’ai vu aucune raison de ne pas le faire de cette façon. Comme vous l’avez dit, cela nettoie l’espace de noms lorsque vous avez plusieurs ensembles de constantes associées.

Semble un bon endroit pour une enum …

 public enum SortEnum { DURATION, PRICE, TIME; }