Создать экземпляр аннотации со значениями по умолчанию в Java

Как я могу создать экземпляр следующей аннотации (со всеми полями, установленными на их значения по умолчанию).

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

Я пробовал new Settings(), но это не работает...


person akuhn    schedule 05.11.2008    source источник


Ответы (7)


Чтобы создать экземпляр, вам нужно создать класс, который реализует:

Например: public class MySettings implements Annotation, Settings

Но вам нужно обратить особое внимание на правильную реализацию equals и hashCode в соответствии с интерфейсом Annotation. http://download.oracle.com/javase/1.5.0/docs/api/java/lang/annotation/Annotation.html

Если вы не хотите реализовывать это снова и снова, взгляните на javax.enterprise.util.AnnotationLiteral. Это часть API-интерфейса CDI (внедрение контекстной зависимости). (@см. код)

Чтобы получить значения по умолчанию, вы можете использовать способ, описанный akuhn (ранее известный как: Adrian). Settings.class.getMethod("a").getDefaultValue()

person Ralph    schedule 15.08.2011
comment
Красивый. Честный. Действительно красивый. Я попробовал это сам, и это заслуживает того, чтобы стать одобренным решением этого вопроса. - person Jérôme Verstrynge; 17.08.2011
comment
Вы правильно понимаете, что это решение требует реализации каждого «метода» аннотации вручную? - person akuhn; 20.08.2011
comment
@Adrian: правильно, вы должны реализовать интерфейс, определенный аннотацией. -- В вашем случае вам понадобятся методы a(), b() и c() - person Ralph; 20.08.2011
comment
По крайней мере, используя Eclipse, вам нужно будет указать полное имя вашей аннотации для реализации. Это уменьшает ошибку компиляции до предупреждения. - person Nicktar; 26.04.2012
comment
java.lang.Annotation не существует, должно ли это быть java.lang.annotation.Annotation? И если да, то какой смысл реализовывать его явно, ведь это и так суперинтерфейс любой аннотации. - person Vampire; 05.09.2019
comment
@Vampire: ты прав: это java.lang.annotation.Annotation (я исправил) - person Ralph; 15.09.2019
comment
@Vampire: И это также правильно, что не требуется указывать имя Annotation в списке реализованных явных интерфейсов. Во всяком случае, я сделал это явным, чтобы пометить класс реализации как класс, который отличается от другого. - person Ralph; 15.09.2019

Вы не можете создать экземпляр, но по крайней мере получите значения по умолчанию

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());
person akuhn    schedule 05.03.2009
comment
Это неправильно, что Вы не можете создать экземпляр аннотации! Вы можете создать экземпляр аннотации. Вам нужно только создать класс, реализующий java.lang.annotation.Annotation и конкретный интерфейс аннотаций (например, Settings), а затем вы можете создать экземпляр этого класса. Смотрите мой ответ для более подробной информации - person Ralph; 15.08.2011
comment
Спасибо за ваше решение! Технически вы создаете экземпляр подкласса аннотации, как это делает прокси во время выполнения. - person akuhn; 20.08.2011
comment
@akuhn, вы также можете добавить @SuppressWarnings (не проверено) в фабричный метод; в то время как typecast с подавленным предупреждением не выглядит чистым, но мне нравится ваше решение, поскольку оно действительно безопасно для типов и позволяет безопасно использовать функции рефакторинга IDE. - person Sergiy Sokolenko; 11.02.2015
comment
Спасибо! Работает хорошо, но из-за этого он падает, когда есть примитивные элементы без значений по умолчанию. Используя com.google.common.base.Defaults, вы можете сделать: return method.getDefaultValue() != null ? method.getDefaultValue() : Defaults.defaultValue(method.getReturnType()); - person Hans-Peter Störr; 12.04.2019

Я скомпилировал и запустил ниже с удовлетворительными результатами.

class GetSettings {
    public static void main (String[] args){
      @Settings final class c { }
      Settings settings = c.class.getAnnotation(Settings.class);
      System.out.println(settings.aaa());
    }
}
person emory    schedule 26.05.2010
comment
Местные классы, я все время о них забываю, хороший лайфхак! - person akuhn; 26.05.2010
comment
На самом деле этот выглядит, вероятно, лучше, чем текущий принятый ответ, поскольку он полностью сохраняет статическую типизацию. Принимая во внимание, что текущий принятый ответ основан на именах методов в String. - person SaM; 09.05.2011
comment
@SaM не совсем, принятое решение вызывает getDefaultValue для объекта метода, переданного прокси. Это то же самое, как аннотации реализуются самой Java, поэтому они так же безопасны для типов, как и ваши. (Исходные примеры кода, в которых для выбора метода используется строка, используются только для иллюстрации принципа.) - person akuhn; 24.07.2011
comment
Великолепно! Именно то, что мне было нужно. - person Pierre D; 16.03.2012
comment
@akuhn Это еще лучше. Без ошибок в названии метода. Нет необходимости приводить DefaultValue к String. - person Stefan; 26.03.2013
comment
Это здорово, но не работает, если политика хранения SOURCE :/ - person Matthew; 14.07.2016

была такая же проблема, я решил ее следующим образом.

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

Если используется с методом:

@Settings
public void myMethod() {
}

Теперь ваша аннотация инициализирована значениями по умолчанию.

person Florin    schedule 05.11.2008

Это работает с Sun/Oracle Java 5,6,7,8: (но потенциально может сломаться с Java 9 из-за задействованных классов солнца). // редактировать Только что проверил, что это все еще работает с 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)
person Thomas Darimont    schedule 17.04.2015

Существует альтернативное решение, если вы можете позволить себе изменить тело класса 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 (да, имя получше!).

person mindas    schedule 11.07.2016