Java allows enum
as values for annotation values. How can I define a kind of generic default enum
value for an enum
annotation value?
I have considered the following, but it won't compile:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public <T extends Enum<T>> @interface MyAnnotation<T> {
T defaultValue();
}
Is there a solution to this issue or not?
BOUNTY
Is does not seem like there is a direct solution to this Java corner case. So, I am starting a bounty to find the most elegant solution to this issue.
The ideal solution should ideally meet the following criteria:
BEST SOLUTION SO FAR
By Dunes:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface MyAnnotation {
// By not specifying default,
// we force the user to specify values
Class<? extends Enum<?>> enumClazz();
String defaultValue();
}
...
public enum MyEnumType {
A, B, D, Q;
}
...
// Usage
@MyAnnotation(enumClazz=MyEnumType.class, defaultValue="A");
private MyEnumType myEnumField;
Of course, we can't force the user to specify a valid default value at compile time. However, any annotation pre-processing can verify this with valueOf()
.
IMPROVEMENT
Arian provides an elegant solution to get rid of clazz
in annotated fields:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface MyAnnotation {
}
...
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@MyAnnotation()
public @interface MyEnumAnnotation {
MyEnumType value(); // no default has user define default value
}
...
@MyEnumAnnotation(MyEnum.FOO)
private MyEnumType myValue;
The annotation processor should search for both MyEnumAnnotation
on fields for the provided default value.
This requires the creation of one annotation type per enum type, but guarantees compile time checked type safety.
I'm not sure what your use case is, so I have two answers:
Answer 1:
If you just want to write as little code as possible, here is my suggestion extending Dunes' answer:
public enum ImplicitType {
DO_NOT_USE;
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface MyAnnotation {
Class<? extends Enum<?>> clazz() default ImplicitType.class;
String value();
}
@MyAnnotation("A");
private MyEnumType myEnumField;
When clazz
is ImplicitType.class
, use the fields type as enum class.
Answer 2:
If you want to do some framework magic and want to maintain compiler checked type safety, you can do something like this:
/** Marks annotation types that provide MyRelevantData */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface MyAnnotation {
}
And in the client code, you would have
/** Provides MyRelevantData for TheFramework */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@MyAnnotation
public @interface MyEnumAnnotation {
MyEnumType value(); // default MyEnumType.FOO;
}
@MyEnumAnnotation(MyEnum.FOO)
private MyEnumType myValue;
In this case you would scan the field for annotations which again are annotated with MyAnnotation
. You will have to access the value via reflection on the annotation object, though. Seems like this approach is more complex on the framework side.