Comment fournir la valeur Enum à une annotation à partir d’une constante en Java

Je ne parviens pas à utiliser un Enum extrait d’une constante en tant que paramètre dans une annotation. J’ai cette erreur de compilation: “La valeur de l’atsortingbut d’annotation [atsortingbut] doit être une expression constante enum”.

Ceci est une version simplifiée du code pour le Enum:

public enum MyEnum { APPLE, ORANGE } 

Pour l’annotation:

 @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD }) public @interface MyAnnotation { Ssortingng theSsortingng(); int theInt(); MyEnum theEnum(); } 

Et la classe:

 public class Sample { public static final Ssortingng STRING_CONSTANT = "hello"; public static final int INT_CONSTANT = 1; public static final MyEnum MYENUM_CONSTANT = MyEnum.APPLE; @MyAnnotation(theEnum = MyEnum.APPLE, theInt = 1, theSsortingng = "hello") public void methodA() { } @MyAnnotation(theEnum = MYENUM_CONSTANT, theInt = INT_CONSTANT, theSsortingng = STRING_CONSTANT) public void methodB() { } } 

L’erreur n’apparaît que dans “theEnum = MYENUM_CONSTANT” sur methodB. Les constantes Ssortingng et Int sont correctes avec le compilateur, la constante Enum ne l’est pas, même si c’est exactement la même valeur que celle de methodA. Il me semble que c’est une fonctionnalité manquante dans le compilateur, car les trois sont évidemment des constantes. Il n’y a pas d’appels de méthode, pas d’utilisation étrange de classes, etc.

Ce que je veux réaliser c’est:

  • Pour utiliser le MYENUM_CONSTANT dans l’annotation et plus tard dans le code.
  • Pour restr en sécurité.

Tout moyen d’atteindre ces objectives serait bien.

Modifier:

Merci a tous. Comme vous le dites, cela ne peut pas être fait. Le JLS devrait être mis à jour. J’ai décidé d’oublier les énumérations dans les annotations cette fois et d’utiliser des constantes d’int normales. Tant que l’int est atsortingbué à partir d’une constante nommée, les valeurs sont limitées et son type est “en quelque sorte” sûr.

Cela ressemble à ceci:

 public interface MyEnumSimulation { public static final int APPLE = 0; public static final int ORANGE = 1; } ... public static final int MYENUMSIMUL_CONSTANT = MyEnumSimulation.APPLE; ... @MyAnnotation(theEnumSimulation = MYENUMSIMUL_CONSTANT, theInt = INT_CONSTANT, theSsortingng = STRING_CONSTANT) public void methodB() { ... 

Et je peux utiliser MYENUMSIMUL_CONSTANT n’importe où ailleurs dans le code.

Il semble être défini dans le JLS # 9.7.1 :

[…] Le type de V est compatible avec l’affectation (§5.2) avec T, et de plus:

  • […]
  • Si T est un type enum et V une constante enum.

Et une constante enum est définie comme la constante d’énumération réelle ( JLS # 8.9.1 ), et non comme une variable qui pointe vers cette constante.

Ligne du bas: si vous souhaitez utiliser un enum comme paramètre pour votre annotation, vous devez lui donner une valeur explicite MyEnum.XXXX. Si vous souhaitez utiliser une variable, vous devrez choisir un autre type (pas un enum).

Une solution de contournement possible consiste à utiliser un object Ssortingng ou int que vous pouvez ensuite mapper sur votre enum – vous perdrez la sécurité du type mais les erreurs peuvent être facilement repérées au moment de l’exécution (= pendant les tests).

“Tous les problèmes en informatique peuvent être résolus par un autre niveau d’indirection” — David Wheeler

C’est ici:

Classe Enum:

 public enum Gender { MALE(Constants.MALE_VALUE), FEMALE(Constants.FEMALE_VALUE); Gender(Ssortingng genderSsortingng) { } public static class Constants { public static final Ssortingng MALE_VALUE = "MALE"; public static final Ssortingng FEMALE_VALUE = "FEMALE"; } } 

Classe de personne:

 import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; import static com.fasterxml.jackson.annotation.JsonTypeInfo.As; import static com.fasterxml.jackson.annotation.JsonTypeInfo.Id; @JsonTypeInfo(use = Id.NAME, include = As.PROPERTY, property = Person.GENDER) @JsonSubTypes({ @JsonSubTypes.Type(value = Woman.class, name = Gender.Constants.FEMALE_VALUE), @JsonSubTypes.Type(value = Man.class, name = Gender.Constants.MALE_VALUE) }) public abstract class Person { ... } 

Je pense que la réponse la plus votée est incomplète, car elle ne garantit pas du tout que la valeur enum est associée à la valeur de Ssortingng constante sous-jacente . Avec cette solution, il suffit de découpler les deux classes.

Au lieu de cela, je suggère plutôt de renforcer le couplage montré dans cette réponse en imposant la corrélation entre le nom enum et la valeur constante comme suit:

 public enum Gender { MALE(Constants.MALE_VALUE), FEMALE(Constants.FEMALE_VALUE); Gender(Ssortingng genderSsortingng) { if(!genderSsortingng.equals(this.name())) throw new IllegalArgumentException(); } public static class Constants { public static final Ssortingng MALE_VALUE = "MALE"; public static final Ssortingng FEMALE_VALUE = "FEMALE"; } } 

De plus, les tests unitaires doivent être écrits en vérifiant que les chaînes sont des constantes.

La règle de contrôle semble être “Si T est un type enum et V est une constante enum.”, 9.7.1. Annotations normales . D’après le texte, le JLS vise une évaluation extrêmement simple des expressions dans les annotations. Une constante enum est spécifiquement l’identifiant utilisé dans la déclaration enum.

Même dans d’autres contextes, une finale initialisée avec une constante enum ne semble pas être une expression constante. 4.12.4. final Variables dit “Une variable de type ou type primitif Ssortingng, qui est finale et initialisée avec une expression constante de compilation (§15.28), est appelée une variable constante.”, mais n’inclut pas une finale de type enum initialisée avec un enum constant.

J’ai également testé un cas simple dans lequel il importe de savoir si une expression est une expression constante – un entourage d’une affectation à une variable non atsortingbuée. La variable n’a pas été assignée. Une autre version du même code qui a testé un final int a en fait affecté la variable:

  public class Bad { public static final MyEnum x = MyEnum.AAA; public static final int z = 3; public static void main(Ssortingng[] args) { int y; if(x == MyEnum.AAA) { y = 3; } // if(z == 3) { // y = 3; // } System.out.println(y); } enum MyEnum { AAA, BBB, CCC } } 

Je cite de la dernière ligne de la question

Tout moyen d’atteindre ces objectives serait bien.

J’ai donc essayé ceci

  1. Ajout d’un paramètre enumType à l’annotation en tant qu’espace réservé

     @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD }) public @interface MyAnnotation { Ssortingng theSsortingng(); int theInt(); MyAnnotationEnum theEnum() default MyAnnotationEnum.APPLE; int theEnumType() default 1; } 
  2. Ajout d’une méthode getType dans l’implémentation

     public enum MyAnnotationEnum { APPLE(1), ORANGE(2); public final int type; private MyAnnotationEnum(int type) { this.type = type; } public final int getType() { return type; } public static MyAnnotationEnum getType(int type) { if (type == APPLE.getType()) { return APPLE; } else if (type == ORANGE.getType()) { return ORANGE; } return APPLE; } } 
  3. Fait un changement pour utiliser une constante int au lieu de la enum

     public class MySample { public static final Ssortingng STRING_CONSTANT = "hello"; public static final int INT_CONSTANT = 1; public static final int MYENUM_TYPE = 1;//MyAnnotationEnum.APPLE.type; public static final MyAnnotationEnum MYENUM_CONSTANT = MyAnnotationEnum.getType(MYENUM_TYPE); @MyAnnotation(theEnum = MyAnnotationEnum.APPLE, theInt = 1, theSsortingng = "hello") public void methodA() { } @MyAnnotation(theEnumType = MYENUM_TYPE, theInt = INT_CONSTANT, theSsortingng = STRING_CONSTANT) public void methodB() { } } 

Je dérive la constante MYENUM de MYENUM_TYPE int, donc si vous changez MYENUM, il vous suffit de remplacer la valeur int par la valeur de type enum correspondante.

Ce n’est pas la solution la plus élégante, mais je le donne à cause de la dernière ligne de la question.

Tout moyen d’atteindre ces objectives serait bien.

Juste une note de côté, si vous essayez d’utiliser

 public static final int MYENUM_TYPE = MyAnnotationEnum.APPLE.type; 

Le compilateur dit à l’annotation MyAnnotation.theEnumType doit être une constante