Подтвердить что ты не робот

Варианты использования аннотаций

Каковы допустимые варианты использования аннотаций?

При проектировании в основном основанных на аннотации систем конфигурации мне иногда нужно создавать классы, которые реализуют аннотации для генерации кода или программной конфигурации.

Альтернатива включает зеркальное отображение данных, содержащихся в аннотациях, в DTO, что кажется накладными расходами.

Вот пример:

public enum IDType {
    LOCAL,
    URI,
    RESOURCE;
}

@Documented
@Target( { METHOD, FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface Id {
    /**
     * @return
     */
    IDType value() default IDType.LOCAL;
}

с реализацией

public class IdImpl implements Id{

    private final IDType idType;

    public IdImpl(IDType idType){
        this.idType = idType;
    }

    @Override
    public IDType value() {
        return idType;
    }

    @Override
    public Class<? extends Annotation> annotationType() {
        return Id.class;
    }

}

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

Предупреждение для приведенного выше примера

Идентификатор типа аннотации не должен быть используется как суперинтерфейс для IdImpl

Отредактировано:

Я только что нашел этот пример из Guice:

bind(CreditCardProcessor.class)
    .annotatedWith(Names.named("Checkout"))
    .to(CheckoutCreditCardProcessor.class);

Смотрите Javadoc из имен.

Есть ли у кого-нибудь какая-либо информация о том, почему это ограничение существует или есть какие-то другие варианты использования?

4b9b3361

Ответ 1

Я никогда не использовал его на практике, но, что вы получаете, вы можете использовать классы в качестве замены ваших аннотаций.

Позвольте создать искусственный пример. Скажем, у нас есть генератор документации. Он читает аннотацию @Docu от заданных классов и печатает атрибут description. Вот так:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.List;

public class DokuGenerator {

    public static void main(String[] args) throws Exception {
        new DokuGenerator(StaticClass.class, StaticClass2.class);
    }

    public DokuGenerator(Class<?>... classesToDokument) throws Exception {
        List<Docu> documentAnnotations = getDocumentAnnotations(classesToDokument);
        printDocumentation(documentAnnotations);
    }

    private List<Docu> getDocumentAnnotations(Class<?>... classesToDokument)
            throws Exception {
        List<Docu> result = new ArrayList<Docu>();
        for (Class<?> c : classesToDokument)
            if (c.isAnnotationPresent(Docu.class))
                result.add(c.getAnnotation(Docu.class));
        return result;
    }

    private void printDocumentation(List<Docu> toDocument) {
        for (Docu m : toDocument)
            System.out.println(m.description());
    }

}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Docu {
    String description();
}

@Docu(description = "This is a static class!")
class StaticClass {
}

@Docu(description = "This is another static class!")
class StaticClass2 {
}

Печать

This is a static class!  
This is another static class!

То, что мы хотим сделать, состоит в том, что класс может быть не только статически аннотирован, но и может добавлять информацию о времени выполнения в документацию. Мы очень рады использовать аннотацию @Docu большую часть времени, но есть особые случаи, которые мы хотим получить специальную документацию. Возможно, мы захотим добавить документацию производительности для некоторых методов. Мы можем сделать это, разрешив классу реализовать аннотацию. Генератор сначала проверяет аннотацию и, если нет, проверяет, реализует ли класс аннотацию. Если это так, он добавляет класс в список аннотаций.

Подобно этому (только две дополнительные строки кода в генераторе):

import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class DokuGenerator {

    public static void main(String[] args) throws Exception {
        new DokuGenerator(StaticClass.class, StaticClass2.class,
                DynamicClass.class);
    }

    public DokuGenerator(Class<?>... classesToDokument) throws Exception {
        List<Docu> documentAnnotations = getDocumentAnnotations(classesToDokument);
        printDocumentation(documentAnnotations);
    }

    private List<Docu> getDocumentAnnotations(Class<?>... classesToDokument)
            throws Exception {
        List<Docu> result = new ArrayList<Docu>();
        for (Class<?> c : classesToDokument)
            if (c.isAnnotationPresent(Docu.class))
                result.add(c.getAnnotation(Docu.class));
            else if (Arrays.asList(c.getInterfaces()).contains(Docu.class))
                result.add((Docu) c.newInstance());
        return result;
    }

    private void printDocumentation(List<Docu> toDocument) {
        for (Docu m : toDocument)
            System.out.println(m.description());
    }

}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Docu {
    String description();
}

@Docu(description = "This is a static class!")
class StaticClass {
}

@Docu(description = "This is another static class!")
class StaticClass2 {
}

class DynamicClass implements Docu {

    public DynamicClass() {
        try {
            Thread.sleep((long) (Math.random() * 100));
        } catch (InterruptedException e) {
            // ignore exception to make debugging a little harder
        }
    }

    @Override
    public String description() {
        long millis = System.currentTimeMillis();
        new DynamicClass();
        millis = System.currentTimeMillis() - millis;
        return "This is a dynamic class. I run on "
                + System.getProperty("os.name")
                + ". The construction of an instance of this class run for "
                + millis + " milliseconds.";
    }

    @Override
    public Class<? extends Annotation> annotationType() {
        return Docu.class;
    }

}

Выход:

This is a static class!  
This is another static class!  
This is a dynamic class. I run on Windows XP. The construction of an instance of this class run for 47 milliseconds.

Вам не нужно сильно изменять генератор кода, потому что вы можете использовать класс в качестве замены аннотации.

Другой пример, который должен быть структурой, которая использует аннотации или XML как конфигурацию. У вас может быть один процессор, который работает с аннотациями. Если вы используете XML как конфигурацию, вы можете создавать экземпляры классов, которые реализуют аннотации, и ваш процессор работает над ними без единого изменения! (конечно, есть и другие способы достижения такого же эффекта, но это ОДИН способ сделать это)

Ответ 2

JAXBIntroductions является хорошим примером: он позволяет настраивать аннотации JAXB с использованием файлов XML. Приходят в голову два основных случая использования: настройка классов у вас нет доступа к источнику или разных конфигураций для одного класса.

В общем, я думаю, что динамическое создание экземпляров аннотаций для передачи их в фреймворки обычно является хорошим вариантом использования. Однако, если вы являетесь разработчиком этой структуры, я, конечно, подумаю дважды.

Ответ 3

Я использую его, когда создаю аннотацию и хочу сделать ее использование необязательным, указав по умолчанию.

Использовать ваш пример; Когда я обрабатываю/просматриваю следующий beans, я хочу использовать значение по умолчанию для BeanB.

@Id
class BeanA {}

// No annotation
class BeanB {}

Реализация по умолчанию;

private static final Id DEFAULT_ID = new Id() {

    @Override
    public IDType value() {
        return IDType.LOCAL;
    }

    @Override
    public Class<? extends Annotation> annotationType() {
        return Id.class;
    }
};

Обработка

Id beanId = (bean.getClass().isAnnotationPresent(Id.class))
    ? bean.getClass().getAnnotation(Id.class)
    : DEFAULT_ID;

Ответ 4

Для этого нет действительных случаев для пользователей - компилятор просто оповещает об этом, так как было бы довольно беспорядочно запрещать это, и люди, которые пишут компиляторы, могут нуждаться в этом объекте в очень редком случае. Если вам нужно классифицировать аннотации, ознакомьтесь с этой статьей, чтобы узнать, как это сделать: Почему нельзя расширять аннотации в Java?

Представьте бедную душу, пришедшую после вас, чтобы поддерживать и отлаживать этот код или другого, кому нужно написать инструмент для кодогенеза, и предполагает, что типы аннотаций прямые или другие, которые просто использовали такую ​​аннотацию, даже не мечтали о том, что может произойти и что делать об этом. К тому времени, когда он обнаруживает этот взлом и находит способ его устранить, он умрет от грыжи - или эквивалентного недомогания: -) Ожидается, что аннотации будут чисто декларативными утверждениями, которые интерпретируются исключительно с помощью инструмента codegen, который выполняется отдельно от аннотированного кода и рассматривает его как данные.

Взгляните на этот код и попробуйте честно сказать, какой рациональный расон для чего-то вроде:

 public Class<? extends Annotation> annotationType() { 
     return Id.class; 
 } 

и это будет небольшая вещь по сравнению с тем, что люди могут ввести код.

Аннотации - это не место для практики взлома - что компилятор пытается передать. Вы точно знаете, когда и как может выполняться код в "реализации" аннотации? В том числе CTOR? Что доступно, а что нет? Что можно назвать безопасным? У компилятора нет - для компилятора для проверки фактической безопасности такого взлома потребовался бы довольно тяжелый статический анализ. Поэтому вместо этого он просто выдает предупреждение, так что когда что-то пойдет не так, люди не могут обвинять компиляцию, VM и все остальное.