Есть ли что-то вроде наследования аннотаций в java?

119

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

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

@Target(value = {ElementType.TYPE})
public @interface Move extends Page{
 String method1();
 String method2();
}

И это будет обычная аннотация:

@Target(value = {ElementType.TYPE})
public @interface Page{
 String method3();
}

В приведенном выше примере я ожидал, что Move унаследует method3, но получаю предупреждение о том, что extends недействителен с аннотациями. Я пытался, чтобы аннотация расширяла общую базовую, но это не сработало. Это вообще возможно или это просто проблема дизайна?

javydreamercsw
источник
3
Наследование аннотаций кажется обязательным для создания DSL на основе аннотаций. Жалко, что наследование аннотаций не поддерживается.
Ceki
2
Я согласен, это кажется естественным занятием. Вы ожидаете, что это применимо ко всему, особенно после того, как разобрались с наследованием в Java.
javydreamercsw

Ответы:

76

К сожалению нет. Очевидно, это как-то связано с программами, которые читают аннотации класса, не загружая их полностью. См. Почему в Java нельзя расширять аннотации?

Однако типы наследуют аннотации своего суперкласса, если эти аннотации наследуются @Inherited.

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

@Move
@Page
public class myAwesomeClass {}

Есть ли причина, по которой у вас не получится?

andronikus
источник
1
Я так думал, но пытался все упростить. Может быть, применение Common к абстрактному классу поможет ...
javydreamercsw
1
Да, это тоже выглядело довольно умно. Удачи!
andronikus
67

Вы можете аннотировать свою аннотацию с помощью базовой аннотации вместо наследования. Это используется в среде Spring .

Чтобы привести пример

@Target(value = {ElementType.ANNOTATION_TYPE})
public @interface Vehicle {
}

@Target(value = {ElementType.TYPE})
@Vehicle
public @interface Car {
}

@Car
class Foo {
}

Затем вы можете проверить, аннотирован ли класс с Vehicleпомощью Spring AnnotationUtils :

Vehicle vehicleAnnotation = AnnotationUtils.findAnnotation (Foo.class, Vehicle.class);
boolean isAnnotated = vehicleAnnotation != null;

Этот метод реализован как:

public static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType) {
    return findAnnotation(clazz, annotationType, new HashSet<Annotation>());
}

@SuppressWarnings("unchecked")
private static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType, Set<Annotation> visited) {
    try {
        Annotation[] anns = clazz.getDeclaredAnnotations();
        for (Annotation ann : anns) {
            if (ann.annotationType() == annotationType) {
                return (A) ann;
            }
        }
        for (Annotation ann : anns) {
            if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) {
                A annotation = findAnnotation(ann.annotationType(), annotationType, visited);
                if (annotation != null) {
                    return annotation;
                }
            }
        }
    }
    catch (Exception ex) {
        handleIntrospectionFailure(clazz, ex);
        return null;
    }

    for (Class<?> ifc : clazz.getInterfaces()) {
        A annotation = findAnnotation(ifc, annotationType, visited);
        if (annotation != null) {
            return annotation;
        }
    }

    Class<?> superclass = clazz.getSuperclass();
    if (superclass == null || Object.class == superclass) {
        return null;
    }
    return findAnnotation(superclass, annotationType, visited);
}

AnnotationUtilsтакже содержит дополнительные методы для поиска аннотаций к методам и другим аннотированным элементам. Класс Spring также достаточно силен для поиска по мостовым методам, прокси-серверам и другим угловым случаям, особенно тем, которые встречаются в Spring.

Григорий Гончар
источник
13
Пожалуйста, включите объяснение того, как обрабатывать такие аннотации.
Александр Дубинский
1
Вы можете использовать Spring AnnotationUtils.findAnnotation (..), см .: docs.spring.io/spring/docs/current/javadoc-api/org/…
rgrebski
2
Когда аннотация A аннотируется другой аннотацией B, и мы аннотируем класс C с помощью A, класс C обрабатывается так, как если бы он аннотирован как A, так и B. Это специфическое поведение среды Spring - AnnotationUtils.findAnnotation творит чудеса здесь и используется для перемещения вверх, чтобы найти аннотации аннотации. Так что не поймите неправильно, что это поведение Java по умолчанию в отношении обработки аннотаций.
qartal
Это возможно только в том случае, если аннотации, которые вы хотите создать, имеют цель TYPEили ANNOTATION_TYPE.
OrangeDog
7

Помимо ответа Григория аннотации аннотации.

Вы можете проверить, например, методы, содержащие @Qualifierаннотацию (или аннотацию, помеченную @Qualifier) с помощью этого цикла:

for (Annotation a : method.getAnnotations()) {
    if (a.annotationType().isAnnotationPresent(Qualifier.class)) {
        System.out.println("found @Qualifier annotation");//found annotation having Qualifier annotation itself
    }
}

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

philnate
источник
Чем это отличается от ответа Григория?
Александр Дубинский
@AleksandrDubinsky: Это гораздо более простая реализация без использования Spring. Он не находит рекурсивно аннотированные аннотации, но я думаю, что обычно это не требуется. Мне нравится простота этого решения.
Стефан Штайнеггер,
1

Посмотрите https://github.com/blindpirate/annotation-magic , библиотеку, которую я разработал, когда у меня возник тот же вопрос.

@interface Animal {
    boolean fluffy() default false;

    String name() default "";
}

@Extends(Animal.class)
@Animal(fluffy = true)
@interface Pet {
    String name();
}

@Extends(Pet.class)
@interface Cat {
    @AliasFor("name")
    String value();
}

@Extends(Pet.class)
@interface Dog {
    String name();
}

@interface Rat {
    @AliasFor(target = Animal.class, value = "name")
    String value();
}

@Cat("Tom")
class MyClass {
    @Dog(name = "Spike")
    @Rat("Jerry")
    public void foo() {
    }
}

        Pet petAnnotation = AnnotationMagic.getOneAnnotationOnClassOrNull(MyClass.class, Pet.class);
        assertEquals("Tom", petAnnotation.name());
        assertTrue(AnnotationMagic.instanceOf(petAnnotation, Animal.class));

        Animal animalAnnotation = AnnotationMagic.getOneAnnotationOnClassOrNull(MyClass.class, Animal.class);
        assertTrue(animalAnnotation.fluffy());

        Method fooMethod = MyClass.class.getMethod("foo");
        List<Animal> animalAnnotations = AnnotationMagic.getAnnotationsOnMethod(fooMethod, Animal.class);
        assertEquals(Arrays.asList("Spike", "Jerry"), animalAnnotations.stream().map(Animal::name).collect(toList()));
user11949000
источник