39

How can I create an instance of the following annotation (with all fields set to their default value).

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

I tried new Settings(), but that does not seem to work...

akuhn
  • 27,477
  • 2
  • 76
  • 91

7 Answers7

41

To create an instance you need to create a class that implements:

For example: public class MySettings implements Annotation, Settings

But you need to pay special attention to the correct implementation of equals and hashCode according to the Annotation interface. http://download.oracle.com/javase/1.5.0/docs/api/java/lang/annotation/Annotation.html

If you do not want to implement this again and again then have a look at the javax.enterprise.util.AnnotationLiteral class. That is part of the CDI(Context Dependency Injection)-API. (@see code)

To get the default values you can use the way that is described by akuhn (former known as: Adrian). Settings.class.getMethod("a").getDefaultValue()

Ralph
  • 118,862
  • 56
  • 287
  • 383
  • 1
    Beautiful. Honest. Really beautiful. I tried it myself and it deserves to become the approved solution for this question. – Jérôme Verstrynge Aug 16 '11 at 20:57
  • Do you get it right that this solution requires to implement each “method” of the annotation manually? – akuhn Aug 20 '11 at 03:00
  • @Adrian: right, you have to implement the Interface that is defined by the Annotation. -- In you case you will need the methods `a()`, `b()` and `c()` – Ralph Aug 20 '11 at 08:42
  • At least using Eclipse, you'll have to provide the fully qualified name of your annotation to implements. That reduces the compile-error to a warning. – Nicktar Apr 26 '12 at 12:11
  • `java.lang.Annotation` does not exist, should this be `java.lang.annotation.Annotation`? And if so, what is the sense in implementing it explicitly, as it is the super-interface of any annotation anyway. – Vampire Sep 05 '19 at 19:35
  • @Vampire: you are right: it is `java.lang.annotation.Annotation` (I fixed it) – Ralph Sep 15 '19 at 07:35
  • @Vampire: And it is also right, that it is *not required* to name `Annotation` in the list of implemented interfaced explicit. Anyway I made it explicit in order to mark the implementation class as class that is different to the other once. – Ralph Sep 15 '19 at 07:47
38

You cannot create an instance, but at least get the default values

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

And then, a dynamic proxy could be used to return the default values. Which is, as far as I can tell, the way annotations are handled by Java itself also.

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());
akuhn
  • 27,477
  • 2
  • 76
  • 91
  • It is wrong that "You cannot create an instance" of an annotation! You can create an instance of an Annotation. You only need to create a class implements java.lang.annotation.Annotation and the concrete annotation interface (for example Settings), and then you can create an instance of this class. See my answer for more details – Ralph Aug 15 '11 at 16:41
  • 1
    Thanks for your solution! Technically you create an instance of a subclass of the annotation, just as does the proxy at runtime. – akuhn Aug 20 '11 at 02:59
  • @akuhn, you might also add @SuppressWarnings("unchecked") to the factory method "of"; while typecast with suppressed warning doesn't look clean, but I like your solution as it really is typesafe and allows to safely use IDE's refactoring features. – Sergiy Sokolenko Feb 11 '15 at 07:35
  • 1
    Thanks! Works nicely but for that it crashes when there are primitive members without defaults. Using com.google.common.base.Defaults you might do: return method.getDefaultValue() != null ? method.getDefaultValue() : Defaults.defaultValue(method.getReturnType()); – Dr. Hans-Peter Störr Apr 12 '19 at 12:48
28

I compile and ran below with satisfactory results.

class GetSettings {
    public static void main (String[] args){
      @Settings final class c { }
      Settings settings = c.class.getAnnotation(Settings.class);
      System.out.println(settings.aaa());
    }
}
emory
  • 10,725
  • 2
  • 30
  • 58
  • Local classes, I keep forgetting about them, nice hack! – akuhn May 26 '10 at 10:10
  • Actually this one looks probably better than the current accepted answer as it keeps the static typing all the way. Whereas the current accepted answer relies on method names in a String. – SaM May 09 '11 at 09:25
  • @SaM not quite, the accepted solution calls `getDefaultValue` on the method object passed to the proxy. This is the same way annotation are implemented by Java itself, so it is just as "type safe" as yours. (The initial code examples that use a string to select the method are only used to illustrate the principle.) – akuhn Jul 23 '11 at 21:39
  • 1
    @akuhn This is still better. No misspelling the method name. No need to cast the DefaultValue to String. – Stefan Mar 25 '13 at 20:57
  • 1
    This is great - but doesn't work if the retention policy is SOURCE :/ – Matthew Jul 14 '16 at 09:36
3

had the same issue, i solved it as follows.

public static FieldGroup getDefaultFieldGroup() {
    @FieldGroup
    class settring {
    }
    return settring.class.getAnnotation(FieldGroup.class);
}
ex0b1t
  • 1,292
  • 1
  • 17
  • 25
2

There is alternative solution, if you can afford to change the body of Settings class:

@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;
}

Then you can simply reference Settings.DEFAULT_A (yes, a better name would help!).

mindas
  • 26,463
  • 15
  • 97
  • 154
1

If used with a method:

@Settings
public void myMethod() {
}

Now your annotation is initialized with default values.

Jason Plank
  • 2,336
  • 5
  • 31
  • 40
Florin
  • 1,379
  • 3
  • 16
  • 21
0

This works with Sun/Oracle Java 5,6,7,8: (but could potentially break with Java 9 due to the sun classes involved). //edit Just verified that this still works with 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;
    }
}

Output:

Custom annotation creation: @demo.AnnotationProxyExample$Example(bar=true, foo=42, value=required)
Traditional annotation creation: @demo.AnnotationProxyExample$Example(bar=true, foo=42, value=required)
Thomas Darimont
  • 1,356
  • 11
  • 14