Ich kann eine Aufzählung aus einer Konstante nicht als Parameter in einer Anmerkung verwenden. Ich erhalte den folgenden Kompilierungsfehler: "Der Wert für das Annotationsattribut [Attribut] muss ein konstanter Enum-Ausdruck sein".
Dies ist eine vereinfachte Version des Codes für die Aufzählung:
public enum MyEnum {
APPLE, ORANGE
}
Für die Anmerkung:
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
public @interface MyAnnotation {
String theString();
int theInt();
MyEnum theEnum();
}
Und die Klasse:
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() {
}
}
Der Fehler wird nur in "theEnum = MYENUM_CONSTANT" über Methode B angezeigt. String- und Int-Konstanten sind mit dem Compiler in Ordnung, die Enum-Konstante jedoch nicht, obwohl sie genau den gleichen Wert wie die über methodA hat. Sieht für mich so aus, als ob dies eine fehlende Funktion im Compiler ist, da alle drei offensichtlich Konstanten sind. Es gibt keine Methodenaufrufe, keine seltsame Verwendung von Klassen usw.
Was ich erreichen möchte, ist:
- Verwendung von MYENUM_CONSTANT sowohl in der Anmerkung als auch später im Code.
- Um typsicher zu bleiben.
Jeder Weg, um diese Ziele zu erreichen, wäre in Ordnung.
Bearbeiten:
Vielen Dank an alle. Wie Sie sagen, kann es nicht getan werden. Das JLS sollte aktualisiert werden. Diesmal habe ich beschlossen, Aufzählungen in Anmerkungen zu vergessen und reguläre int-Konstanten zu verwenden. Solange das int von einer benannten Konstante zugewiesen wird, sind die Werte begrenzt und es ist eine Art Typ sicher.
Es sieht aus wie das:
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() {
...
Und ich kann MYENUMSIMUL_CONSTANT an einer anderen Stelle im Code verwenden.
quelle
Antworten:
Es scheint in JLS # 9.7.1 definiert zu sein :
Und eine Aufzählungskonstante ist definiert als die tatsächliche Aufzählungskonstante ( JLS # 8.9.1 ), keine Variable, die auf diese Konstante zeigt.
Fazit: Wenn Sie eine Aufzählung als Parameter für Ihre Anmerkung verwenden möchten, müssen Sie ihr einen expliziten
MyEnum.XXXX
Wert geben. Wenn Sie eine Variable verwenden möchten, müssen Sie einen anderen Typ auswählen (keine Aufzählung).Eine mögliche Problemumgehung besteht darin, ein
String
oder zu verwendenint
, das Sie dann Ihrer Aufzählung zuordnen können. Sie verlieren die Typensicherheit, aber die Fehler können zur Laufzeit (= während der Tests) leicht erkannt werden.quelle
"Alle Probleme in der Informatik können durch eine andere Indirektionsebene gelöst werden" --- David Wheeler
Hier ist es:
Aufzählungsklasse:
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"; } }
Personenklasse:
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 { ... }
quelle
Ich denke, dass die am häufigsten gewählte Antwort unvollständig ist, da sie überhaupt nicht garantiert, dass der Aufzählungswert mit dem zugrunde liegenden konstanten
String
Wert gekoppelt ist . Mit dieser Lösung sollte man nur die beiden Klassen entkoppeln.Stattdessen schlage ich eher vor, die in dieser Antwort gezeigte Kopplung zu verstärken, indem die Korrelation zwischen dem Namen der Aufzählung und dem konstanten Wert wie folgt erzwungen wird:
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"; } }
Wie von @GhostCat in einem Kommentar hervorgehoben, müssen geeignete Komponententests durchgeführt werden, um die Kopplung sicherzustellen.
quelle
Die Steuerregel scheint zu lauten: "Wenn T ein Aufzählungstyp ist und V eine Aufzählungskonstante.", 9.7.1. Normale Anmerkungen . Aus dem Text geht hervor, dass das JLS eine äußerst einfache Bewertung der Ausdrücke in Anmerkungen anstrebt. Eine Enum-Konstante ist speziell der Bezeichner, der in der Enum-Deklaration verwendet wird.
Selbst in anderen Kontexten scheint ein mit einer Enum-Konstante initialisiertes Finale kein konstanter Ausdruck zu sein. 4.12.4. final Variables sagt "Eine Variable vom primitiven Typ oder Typ String, die final ist und mit einem Konstantenausdruck zur Kompilierungszeit (§15.28) initialisiert wird, wird als konstante Variable bezeichnet.", enthält jedoch kein mit en initialisiertes Finale vom Typ enum Enum-Konstante.
Ich habe auch einen einfachen Fall getestet, in dem es darauf ankommt, ob ein Ausdruck ein konstanter Ausdruck ist - ein Fall, der eine Zuweisung zu einer nicht zugewiesenen Variablen umgibt. Die Variable wurde nicht zugewiesen. Eine alternative Version desselben Codes, der stattdessen ein endgültiges int getestet hat, hat die Variable definitiv zugewiesen:
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 } }
quelle
Ich zitiere aus der letzten Zeile der Frage
Also habe ich es versucht
Der Annotation wurde ein enumType-Parameter als Platzhalter hinzugefügt
@Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD }) public @interface MyAnnotation { String theString(); int theInt(); MyAnnotationEnum theEnum() default MyAnnotationEnum.APPLE; int theEnumType() default 1; }
In der Implementierung wurde eine getType-Methode hinzugefügt
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; } }
Es wurde eine Änderung vorgenommen, um eine int-Konstante anstelle der Aufzählung zu verwenden
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() { } }
Ich leite die MYENUM-Konstante von MYENUM_TYPE int ab. Wenn Sie also MYENUM ändern, müssen Sie nur den int-Wert in den entsprechenden Wert für den Aufzählungstyp ändern.
Es ist nicht die eleganteste Lösung, aber ich gebe es wegen der letzten Zeile in der Frage.
Nur eine Randnotiz, wenn Sie versuchen, zu verwenden
public static final int MYENUM_TYPE = MyAnnotationEnum.APPLE.type;
Der Compiler sagt bei der Annotation - MyAnnotation.theEnumType muss eine Konstante sein
quelle
java.lang.ExceptionInInitializerError Caused by: java.lang.ArrayIndexOutOfBoundsException: 2
Meine Lösung war
public enum MyEnum { FOO, BAR; // element value must be a constant expression // so we needs this hack in order to use enums as // annotation values public static final String _FOO = FOO.name(); public static final String _BAR = BAR.name(); }
Ich dachte, das wäre der sauberste Weg. Dies erfüllt einige Anforderungen:
@Annotation(foo = MyEnum._FOO)
BEARBEITEN
Dies führt gelegentlich zu Kompilierungsfehlern, die zum Grund des Originals führen
element value must be a constant expression
Das ist also anscheinend keine Option!
quelle