【问题标题】:Create Annotation instance with defaults, in Java在 Java 中创建具有默认值的 Annotation 实例
【发布时间】:2008-11-05 22:04:06
【问题描述】:

如何创建以下注释的实例(所有字段都设置为默认值)。

    @Retention( RetentionPolicy.RUNTIME )
    public @interface Settings {
            String a() default "AAA";
            String b() default "BBB";
            String c() default "CCC";
    }

我试过new Settings(),但这似乎不起作用...

【问题讨论】:

标签: java annotations instantiation


【解决方案1】:

要创建一个实例,您需要创建一个实现的类:

例如: public class MySettings implements Annotation, Settings

但是需要特别注意equalshashCode根据Annotation接口的正确实现。 http://download.oracle.com/javase/1.5.0/docs/api/java/lang/annotation/Annotation.html

如果您不想一次又一次地实现这一点,请查看javax.enterprise.util.AnnotationLiteral 类。 这是 CDI(上下文依赖注入)-API 的一部分。 (@see code)

要获取默认值,您可以使用 akuhn(以前称为:Adrian)描述的方式。 Settings.class.getMethod("a").getDefaultValue()

【讨论】:

  • 美丽。诚实。真的很美。我自己尝试过,它应该成为这个问题的认可解决方案。
  • 这个解决方案需要手动实现注解的每个“方法”,您理解了吗?
  • @Adrian:对,你必须实现注解定义的接口。 -- 在你的情况下,你需要a()b()c()方法
  • 至少在使用 Eclipse 时,您必须为实现提供注释的完全限定名称。这将编译错误减少为警告。
  • java.lang.Annotation 不存在,应该是java.lang.annotation.Annotation 吗?如果是这样,那么显式实现它有什么意义,因为它无论如何都是任何注释的超级接口。
【解决方案2】:

你不能创建实例,但至少要获取默认值

Settings.class.getMethod("a").getDefaultValue()
Settings.class.getMethod("b").getDefaultValue()
Settings.class.getMethod("c").getDefaultValue()

然后,可以使用动态代理返回默认值。也就是说,据我所知,注解也是由 Java 本身处理的。

class Defaults implements InvocationHandler {
  public static <A extends Annotation> A of(Class<A> annotation) {
    return (A) Proxy.newProxyInstance(annotation.getClassLoader(),
        new Class[] {annotation}, new Defaults());
  }
  public Object invoke(Object proxy, Method method, Object[] args)
      throws Throwable {
    return method.getDefaultValue();
  }
}

Settings s = Defaults.of(Settings.class);
System.out.printf("%s\n%s\n%s\n", s.a(), s.b(), s.c());

【讨论】:

  • 注解“不能创建实例”是错误的!您可以创建 Annotation 的实例。您只需要创建一个实现java.lang.annotation.Annotation 的类和具体的注解接口(例如Settings),然后就可以创建这个类的实例。更多详情见我的回答
  • 感谢您的解决方案!从技术上讲,您创建注解子类的实例,就像运行时的​​代理一样。
  • @akuhn,您还可以将@SuppressWarnings("unchecked") 添加到工厂方法“of”;虽然带有抑制警告的类型转换看起来并不干净,但我喜欢您的解决方案,因为它确实是类型安全的,并且允许安全地使用 IDE 的重构功能。
  • 谢谢!工作得很好,但是当有没有默认值的原始成员时它会崩溃。使用 com.google.common.base.Defaults 你可能会这样做: return method.getDefaultValue() != null ? method.getDefaultValue() : Defaults.defaultValue(method.getReturnType());
【解决方案3】:

我在下面编译运行,结果令人满意。

class GetSettings {
    public static void main (String[] args){
      @Settings final class c { }
      Settings settings = c.class.getAnnotation(Settings.class);
      System.out.println(settings.aaa());
    }
}

【讨论】:

  • 本地课程,我老是忘记它们,很好的 hack!
  • 实际上这个看起来可能比当前接受的答案更好,因为它一直保持静态类型。而当前接受的答案依赖于字符串中的方法名称。
  • @SaM 不完全是,接受的解决方案在传递给代理的方法对象上调用 getDefaultValue。这与 Java 本身实现注释的方式相同,因此它与您的一样“类型安全”。 (最初使用字符串选择方法的代码示例仅用于说明原理。)
  • @akuhn 这还是更好。没有拼写错误的方法名称。无需将 DefaultValue 转换为 String。
  • 这很好 - 但如果保留策略是 SOURCE 则不起作用:/
【解决方案4】:

遇到同样的问题,我解决了如下。

public static FieldGroup getDefaultFieldGroup() {
    @FieldGroup
    class settring {
    }
    return settring.class.getAnnotation(FieldGroup.class);
}

【讨论】:

    【解决方案5】:

    如果与方法一起使用:

    @Settings
    public void myMethod() {
    }
    

    现在您的注释已使用默认值初始化。

    【讨论】:

      【解决方案6】:

      如果您有能力更改Settings 类的主体,还有其他解决方案:

      @Retention( RetentionPolicy.RUNTIME )
      public @interface Settings {
              String DEFAULT_A = "AAA";
              String DEFAULT_B = "BBB";
              String DEFAULT_C = "CCC";
      
              String a() default DEFAULT_A;
              String b() default DEFAULT_B;
              String c() default DEFAULT_C;
      }
      

      然后您可以简单地引用Settings.DEFAULT_A(是的,更好的名称会有所帮助!)。

      【讨论】:

        【解决方案7】:

        这适用于 Sun/Oracle Java 5、6、7、8:(但由于涉及的 sun 类,可能会与 Java 9 中断)。 //edit 刚刚验证这仍然适用于 OpenJDK 9b59。

        package demo;
        
        import sun.reflect.annotation.AnnotationParser;
        
        import java.lang.annotation.*;
        import java.lang.reflect.Method;
        import java.util.Collections;
        import java.util.HashMap;
        import java.util.Map;
        
        public class AnnotationProxyExample
        {
        
            public static void main(String[] args)
            {
        
                System.out.printf("Custom annotation creation: %s%n", 
                        createAnnotationInstance(Collections.singletonMap("value", "required"), Example.class));
        
                System.out.printf("Traditional annotation creation: %s%n", 
                        X.class.getAnnotation(Example.class));
            }
        
            private static <A extends Annotation> A createAnnotationInstance(Map<String, Object> customValues, Class<A> annotationType)
            {
        
                Map<String, Object> values = new HashMap<>();
        
                //Extract default values from annotation
                for (Method method : annotationType.getDeclaredMethods())
                {
                    values.put(method.getName(), method.getDefaultValue());
                }
        
                //Populate required values
                values.putAll(customValues);
        
                return (A) AnnotationParser.annotationForMap(annotationType, values);
            }
        
            @Example("required")
            static class X
            {
            }
        
            @Retention(RetentionPolicy.RUNTIME)
            @Target(ElementType.TYPE)
            @interface Example
            {
                String value();
                int foo() default 42;
                boolean bar() default true;
            }
        }
        

        输出:

        Custom annotation creation: @demo.AnnotationProxyExample$Example(bar=true, foo=42, value=required)
        Traditional annotation creation: @demo.AnnotationProxyExample$Example(bar=true, foo=42, value=required)
        

        【讨论】:

          猜你喜欢
          • 2015-12-31
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多