Java允许enum
作为注释值的值。如何为enum
注释值定义一种通用的缺省enum
值?
我已经考虑了以下内容,但它不能编译:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public <T extends Enum<T>> @interface MyAnnotation<T> {
T defaultValue();
}
这个问题有没有解决方案?
BOUNTY
对于这种Java死角的情况,似乎没有直接的解决方案。因此,我正在发起一项赏金,以寻找这个问题最优雅的解决方案。
理想的解决方案理想情况下应满足以下标准:
检索默认枚举值作为枚举所需的工作量/复杂度最低
到目前为止的最佳解决方案
作者:
@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;
当然,我们不能强迫用户在编译时指定一个有效的默认值。但是,任何注释预处理都可以用valueOf()
验证这一点。
改进
Arian提供了一个优雅的解决方案来消除带注释字段中的clazz
:
@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;
批注处理器应该在两个MyEnumAnnotation
on字段中搜索所提供的默认值。
这需要为每个枚举类型创建一个注释类型,但保证了编译时检查的类型安全性。
发布于 2011-08-23 19:54:39
我不确定你的用例是什么,所以我有两个答案:
答案1:
如果你只想写尽可能少的代码,这里是我的建议,扩展沙丘的答案:
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;
当clazz
为ImplicitType.class
时,使用字段类型作为枚举类。
答案2:
如果你想做一些框架魔术,并且想要维护编译器检查的类型安全,你可以这样做:
/** Marks annotation types that provide MyRelevantData */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface MyAnnotation {
}
在客户端代码中,您将拥有
/** 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;
在本例中,您将扫描字段中的注释,这些注释再次使用MyAnnotation
进行注释。不过,您必须通过annotation对象上的反射访问值。在框架方面,这种方法似乎更复杂。
发布于 2011-08-16 07:34:40
如果构造函数args中没有提供默认值,那么我不能完全确定您所说的获取默认值是什么意思,并且不关心运行时的泛型类型。
下面的方法是可行的,但这是一个丑陋的技巧。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
public class Main {
@MyAnnotation(clazz = MyEnum.class, name = "A")
private MyEnum value;
public static v oid main(String[] args) {
new Main().printValue();
}
public void printValue() {
System.out.println(getValue());
}
public MyEnum getValue() {
if (value == null) {
value = getDefaultValue("value", MyEnum.class);
}
return value;
}
private <T extends Enum<?>> T getDefaultValue(String name, Class<T> clazz) {
try {
MyAnnotation annotation = Main.class.getDeclaredField(name)
.getAnnotation(MyAnnotation.class);
Method valueOf = clazz.getMethod("valueOf", String.class);
return clazz.cast(valueOf.invoke(this, annotation.value()));
} catch (SecurityException e) {
throw new IllegalStateException(e);
} catch (NoSuchFieldException e) {
throw new IllegalArgumentException(name, e);
} catch (IllegalAccessException e) {
throw new IllegalStateException(e);
} catch (NoSuchMethodException e) {
throw new IllegalStateException(e);
} catch (InvocationTargetException e) {
if (e.getCause() instanceof RuntimeException) {
throw (RuntimeException) e.getCause();
/* rethrow original runtime exception
* For instance, if value = "C" */
}
throw new IllegalStateException(e);
}
}
public enum MyEnum {
A, B;
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface MyAnnotation {
Class<? extends Enum<?>> clazz();
String name();
}
}
编辑:我通过枚举的valueOf方法更改了getDefaultValue,因此如果给定的值不是枚举的引用实例,则会给出更好的错误消息。
发布于 2011-08-16 06:32:51
简单地说,你不能那样做。枚举不能很容易地用作泛型类型;也许有一个例外,那就是枚举实际上可以实现接口,这允许某种程度上的动态使用。但这不适用于注释,因为可以使用的类型集是严格受限的。
https://stackoverflow.com/questions/7071301
复制相似问题