枚举Java枚举值的枚举值

Jér*_*nge 20 java enums annotations default-value

Java允许enum作为注释值的值.如何enumenum注释值定义一种通用默认值?

我考虑过以下内容,但不会编译:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public <T extends Enum<T>> @interface MyAnnotation<T> {

    T defaultValue();

}
Run Code Online (Sandbox Code Playgroud)

这个问题是否有解决方案?

BOUNTY

似乎没有直接解决这个Java角落案例.所以,我正在开始寻找最优雅的解决方案来解决这个问题.

理想的解决方案应该非常符合下列条件:

  1. 所有枚举都可以重复使用一个注释
  2. 从注释实例中检索作为枚举的默认枚举值的最小努力/复杂性

最好的解决方案

通过沙丘:

@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;
Run Code Online (Sandbox Code Playgroud)

当然,我们不能强制用户在编译时指定有效的默认值.但是,任何注释预处理都可以验证这一点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;
Run Code Online (Sandbox Code Playgroud)

注释处理器应在两个MyEnumAnnotation字段上搜索所提供的默认值.

这需要为每个枚举类型创建一个注释类型,但保证编译时检查类型安全.

Dun*_*nes 5

如果你说如果在构造函数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();
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑:我通过枚举的valueOf方法更改了getDefaultValue,因此如果给定的值不是枚举的引用实例,则会给出更好的错误消息.


Cep*_*pod 4

我不确定你的用例是什么,所以我有两个答案:

答案1:

如果您只想编写尽可能少的代码,这是我扩展Dunes答案的建议:

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;
Run Code Online (Sandbox Code Playgroud)

clazz是 时ImplicitType.class,使用字段类型作为枚举类。

答案2:

如果您想做一些框架魔法并希望维护编译器检查的类型安全性,您可以执行以下操作:

/** Marks annotation types that provide MyRelevantData */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface MyAnnotation {
}
Run Code Online (Sandbox Code Playgroud)

在客户端代码中,您将拥有

/** 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;
Run Code Online (Sandbox Code Playgroud)

在这种情况下,您将扫描该字段以查找再次用 进行注释的注释MyAnnotation。不过,您必须通过注释对象上的反射来访问该值。似乎这种方法在框架方面更复杂。