Чи є щось схоже на спадкування анотацій у 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 успадкує метод3, але я отримаю попередження про те, що розширення недійсні для приміток. Я намагався, щоб Анотація розширювала загальну базу, але це не працює. Це навіть можливо чи це лише питання дизайну?


3
Спадщина анотацій здається обов'язковою для створення DSL на основі анотацій. Така шкода, що спадщина анотацій не підтримується.
Чекі

2
Я згоден, здається, що це робити природно. Особливо після того, як ви зрозумієте спадщину на Java, ви сподіваєтесь, що вона стосуватиметься всього.
javydreamercsw

Відповіді:


76

На жаль, немає. Мабуть, це має щось спільне з програмами, які читають примітки на класі, не завантажуючи їх до кінця. Див. Чому не можна розширювати анотації на Java?

Однак типи успадковують анотації свого надкласу, якщо вони є @Inherited.

Крім того, якщо вам не потрібні ці методи взаємодії, ви можете просто скласти примітки до свого класу:

@Move
@Page
public class myAwesomeClass {}

Чи є якась причина, яка б не працювала для вас?


1
Це я вважав, але намагався спростити щось. Можливо, застосування загального до абстрактного класу зробить трюк ...
javydreamercsw

1
Так, це теж виглядало досить розумно. Удачі!
andronikus

67

Ви можете коментувати свої примітки базовою анотацією замість спадкування. Це використовується у весняних рамках .

Навести приклад

@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's AnnotationUtils.findAnnotation (..), див.: Docs.spring.io/spring/docs/current/javadoc-api/org/…
rgrebski

2
Коли анотацію A позначається іншою анотацією B, а ми коментуємо клас C за допомогою A, клас C трактується так, як ніби він анотований як з A, так і з B. Це специфічна поведінка весняних рамок - AnnotationUtils.findAnnotation робить магію тут і використовується для пошуку для ознайомлення з анотаціями. Тому не розумійте, що це поведінка Java за замовчуванням щодо обробки анотацій.
квартал

Це можливо лише в тому випадку, якщо анотації, які ви хочете скласти, мають ціль 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, щоб також працювати.


Чим це відрізняється від відповіді Григорія?
Олександр Дубінський

@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()));
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.