que - recorrer enum java
Cómo suministrar el valor de Enum a una anotación de una constante en Java (5)
No puedo usar un Enum tomado de una constante como parámetro en una anotación. Obtengo este error de compilación: "El valor para el atributo de anotación [atributo] debe ser una expresión constante enum".
Esta es una versión simplificada del código para el Enum:
public enum MyEnum {
APPLE, ORANGE
}
Para la anotación:
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
public @interface MyAnnotation {
String theString();
int theInt();
MyEnum theEnum();
}
Y la clase:
public class Sample {
public static final String STRING_CONSTANT = "hello";
public static final int INT_CONSTANT = 1;
public static final MyEnum MYENUM_CONSTANT = MyEnum.APPLE;
@MyAnnotation(theEnum = MyEnum.APPLE, theInt = 1, theString = "hello")
public void methodA() {
}
@MyAnnotation(theEnum = MYENUM_CONSTANT, theInt = INT_CONSTANT, theString = STRING_CONSTANT)
public void methodB() {
}
}
El error aparece solo en "theEnum = MYENUM_CONSTANT" sobre el métodoB. Las constantes de cadena e int están bien con el compilador, la constante de Enum no lo es, aunque tiene exactamente el mismo valor que el método uno. Me parece que esta es una característica que falta en el compilador, porque las tres son obviamente constantes. No hay llamadas de método, uso extraño de clases, etc.
Lo que quiero lograr es:
- Para usar MYENUM_CONSTANT tanto en la anotación como en el código posterior.
- Para mantenerte seguro.
Cualquier forma de lograr estos objetivos estaría bien.
Editar:
Gracias a todos. Como dices, no se puede hacer. El JLS debe ser actualizado. Decidí olvidarme de enumeraciones en anotaciones esta vez y usar constantes int regulares. Siempre que el int se asigne desde una constante nombrada, los valores son acotados y es seguro para "tipo de" tipo.
Se parece a esto:
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, theString = STRING_CONSTANT)
public void methodB() {
...
Y puedo usar MYENUMSIMUL_CONSTANT en cualquier otro lugar del código.
"Todos los problemas en la informática pueden ser resueltos por otro nivel de indirección" --- David Wheeler
Aquí está:
Clase Enum:
public enum Gender {
MALE(Constants.MALE_VALUE), FEMALE(Constants.FEMALE_VALUE);
Gender(String genderString) {
}
public static class Constants {
public static final String MALE_VALUE = "MALE";
public static final String FEMALE_VALUE = "FEMALE";
}
}
Clase de persona:
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 {
...
}
Cito de la última línea en la pregunta
Cualquier forma de lograr estos objetivos estaría bien.
Así que probé esto
Se agregó un parámetro enumType a la anotación como marcador de posición
@Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD }) public @interface MyAnnotation { String theString(); int theInt(); MyAnnotationEnum theEnum() default MyAnnotationEnum.APPLE; int theEnumType() default 1; }
Se agregó un método getType en la implementación
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; } }
Hizo un cambio para usar una constante int en lugar de la enumeración
public class MySample { public static final String 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, theString = "hello") public void methodA() { } @MyAnnotation(theEnumType = MYENUM_TYPE, theInt = INT_CONSTANT, theString = STRING_CONSTANT) public void methodB() { } }
Obtengo la constante MYENUM de MYENUM_TYPE int, por lo que si cambias MYENUM solo necesitas cambiar el valor int al valor de tipo de enumeración correspondiente.
No es la solución más elegante, pero la estoy dando por la última línea de la pregunta.
Cualquier forma de lograr estos objetivos estaría bien.
Solo una nota al margen, si intentas usar
public static final int MYENUM_TYPE = MyAnnotationEnum.APPLE.type;
El compilador dice en la anotación: MyAnnotation.theEnumType debe ser una constante
Creo que la respuesta más votada es incompleta, ya que no garantiza en absoluto que el valor enum esté acoplado con el valor de la String
constante subyacente . Con esa solución, uno debería desacoplar las dos clases.
En su lugar, sugiero que se refuerce el acoplamiento que se muestra en esa respuesta al imponer la correlación entre el nombre de la enumeración y el valor constante de la siguiente manera:
public enum Gender {
MALE(Constants.MALE_VALUE), FEMALE(Constants.FEMALE_VALUE);
Gender(String genderString) {
if(!genderString.equals(this.name()))
throw new IllegalArgumentException();
}
public static class Constants {
public static final String MALE_VALUE = "MALE";
public static final String FEMALE_VALUE = "FEMALE";
}
}
Además, las pruebas unitarias deben escribirse comprobando que las cadenas sean constantes.
La regla de control parece ser "Si T es un tipo enum, y V es una constante enum", 9.7.1. Anotaciones normales . Del texto, parece que el JLS apunta a una evaluación extremadamente simple de las expresiones en las anotaciones. Una constante enum es específicamente el identificador utilizado dentro de la declaración enum.
Incluso en otros contextos, un final inicializado con una constante enum no parece ser una expresión constante. 4.12.4. Variables finales dice "Una variable de tipo primitivo o tipo String, que es definitiva e inicializada con una expresión constante en tiempo de compilación (§15.28), se llama variable constante.", pero no incluye un final de tipo enum inicializado con un enum constante.
También probé un caso simple en el que importa si una expresión es una expresión constante, y si rodea una asignación a una variable no asignada. La variable no se asignó. Una versión alternativa del mismo código que probó una int final hizo que la variable se asignara definitivamente:
public class Bad {
public static final MyEnum x = MyEnum.AAA;
public static final int z = 3;
public static void main(String[] args) {
int y;
if(x == MyEnum.AAA) {
y = 3;
}
// if(z == 3) {
// y = 3;
// }
System.out.println(y);
}
enum MyEnum {
AAA, BBB, CCC
}
}
Parece estar definido en el JLS # 9.7.1 :
[...] El tipo de V es compatible con la asignación (§5.2) con T, y además:
- [...]
- Si T es un tipo enum, y V es una constante enum.
Y una constante enum se define como la constante enum real ( JLS # 8.9.1 ), no como una variable que apunta a esa constante.
En pocas palabras: si desea utilizar una enumeración como parámetro para su anotación, deberá asignarle un valor explícito de MyEnum.XXXX. Si desea usar una variable, deberá elegir otro tipo (no una enumeración).
Una solución posible es utilizar un String o int que luego puede asignar a su enumeración: perderá el tipo de seguridad, pero los errores se pueden detectar fácilmente en el tiempo de ejecución (= durante las pruebas).