web-dev-qa-db-fra.com

Créer une instance d'annotation avec des valeurs par défaut, en Java

Comment créer une instance de l'annotation suivante (avec tous les champs définis sur leur valeur par défaut).

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

J'ai essayé new Settings(), mais cela ne semble pas fonctionner ...

32
akuhn

Vous ne pouvez pas créer une instance, mais au moins obtenir les valeurs par défaut

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

Et puis, un proxy dynamique pourrait être utilisé pour renvoyer les valeurs par défaut. Autant que je sache, Java gère également les annotations.

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());
35
akuhn

Pour créer une instance, vous devez créer une classe qui implémente:

  • Java.lang.Annotation
  • et l'annotation que vous voulez "simuler"

Par exemple: public class MySettings implements Annotation, Settings

Mais vous devez accorder une attention particulière à la correct implémentation de equals et hashCode en fonction de l'interface Annotation. Http://download.Oracle.com/javase/1,5.0 /docs/api/Java/lang/annotation/Annotation.html

Si vous ne voulez pas implémenter cela encore et encore, jetez un coup d'œil à la classe javax.enterprise.util.AnnotationLiteral . Cela fait partie de CDI (Context Dependency Injection) -API . (@voir code) }

Pour obtenir les valeurs par défaut, vous pouvez utiliser la manière décrite par Adrian. Settings.class.getMethod("a").getDefaultValue()

39
Ralph

J'ai compilé et couru ci-dessous avec des résultats satisfaisants.

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

eu le même problème, je l'ai résolu comme suit.

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

Si utilisé avec une méthode:

@Settings
public void myMethod() {
}

Votre annotation est maintenant initialisée avec les valeurs par défaut.

1
Florin

Cela fonctionne avec Sun/Oracle Java 5,6,7,8: (mais pourrait éventuellement rompre avec Java 9 en raison des classes Sun concernées). // edit Nous venons de vérifier que cela fonctionne toujours avec 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;
    }
}

Sortie:

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

Il existe une solution alternative, si vous pouvez vous permettre de changer le corps de la classe 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;
}

Ensuite, vous pouvez simplement référencer Settings.DEFAULT_A (oui, un meilleur nom aiderait!).

0
mindas