Czy jest coś takiego jak dziedziczenie adnotacji w Javie?


119

Badam adnotacje i doszedłem do punktu, w którym niektóre adnotacje wydają się mieć hierarchię między nimi.

Używam adnotacji do generowania kodu w tle dla kart. Istnieją różne typy kart (a więc inny kod i adnotacje), ale są pewne elementy, które są między nimi wspólne, takie jak nazwa.

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

I to byłaby wspólna adnotacja:

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

W powyższym przykładzie spodziewałbym się, że Move odziedziczy method3, ale pojawia się ostrzeżenie, że extends nie jest poprawne w przypadku adnotacji. Próbowałem, aby adnotacja była rozszerzeniem wspólnej bazy, ale to nie działa. Czy to w ogóle możliwe, czy tylko kwestia projektu?


3
Dziedziczenie adnotacji wydaje się być niezbędne do tworzenia DSL na podstawie adnotacji. Szkoda, że ​​dziedziczenie adnotacji nie jest obsługiwane.
— Ceki

2
Zgadzam się, wydaje się naturalne. Zwłaszcza po zrozumieniu dziedziczenia w Javie można oczekiwać, że będzie miało zastosowanie do wszystkiego.
— javydreamercsw

Odpowiedzi:


76

Niestety nie. Najwyraźniej ma to coś wspólnego z programami, które odczytują adnotacje w klasie bez ich ładowania. Zobacz Dlaczego nie można rozszerzać adnotacji w języku Java?

Jednak typy dziedziczą adnotacje swojej nadklasy, jeśli te adnotacje są @Inherited.

Ponadto, jeśli nie potrzebujesz tych metod do interakcji, możesz po prostu ułożyć adnotacje w swojej klasie:

@Move
@Page
public class myAwesomeClass {}

Czy jest jakiś powód, dla którego nie zadziała?


1
Tak myślałem, ale starałem się wszystko uprościć. Może zastosowanie tej wspólnej do klasy abstrakcyjnej
— załatwi sprawę

1
Tak, to też wyglądało całkiem sprytnie. Powodzenia!
— andronikus

67

Możesz dodać adnotację do adnotacji bazowej zamiast dziedziczenia. Jest to używane w ramach Spring .

Dać przykład

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

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

@Car
class Foo {
}

Następnie możesz sprawdzić, czy klasa jest opatrzona adnotacją za Vehiclepomocą AnnotationUtils Springa :

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

Ta metoda jest implementowana jako:

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);
}

AnnotationUtilszawiera również dodatkowe metody wyszukiwania adnotacji w metodach i innych elementach z adnotacjami. Klasa Spring jest również wystarczająco potężna, aby przeszukiwać metody mostkowane, serwery proxy i inne przypadki narożne, szczególnie te napotkane w Spring.


13
Dołącz wyjaśnienie, jak należy przetwarzać takie adnotacje.
— Aleksandr Dubinsky

1
Możesz użyć Spring's AnnotationUtils.findAnnotation (..), zobacz: docs.spring.io/spring/docs/current/javadoc-api/org/…
— rgrebski

2
Kiedy adnotacja A jest adnotowana inną adnotacją B, a my adnotujemy klasę C za pomocą A, klasa C jest traktowana tak, jakby była opatrzona adnotacją zarówno z adnotacją A, jak i B. Jest to specyficzne zachowanie platformy Spring - AnnotationUtils.findAnnotation robi magię tutaj i służy do przechodzenia w górę w celu znalezienia adnotacji adnotacji. Więc nie zrozumcie źle, że jest to domyślne zachowanie Javy dotyczące obsługi adnotacji.
— qartal

Jest to możliwe tylko wtedy, gdy adnotacje, które chcesz utworzyć, mają cel TYPElub ANNOTATION_TYPE.
— OrangeDog

7

Oprócz odpowiedzi Grygoriys z adnotacjami.

Możesz sprawdzić np. Metody zawierające @Qualifieradnotację (lub adnotację opatrzoną adnotacją @Qualifier) za pomocą tej pętli:

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

Zasadniczo robisz to, aby uzyskać wszystkie adnotacje obecne w metodzie, a z tych adnotacji otrzymasz ich typy i sprawdź te typy, jeśli są adnotowane @Qualifier. Aby ta adnotacja działała, również musi być włączona opcja Target.Annotation_type.


Czym to się różni od odpowiedzi Grygoriy?
— Aleksandr Dubinsky

@AleksandrDubinsky: To po prostu dużo prostsza implementacja bez użycia Springa. Nie znajduje rekursywnie adnotacji z adnotacjami, ale myślę, że zwykle nie jest to wymagane. Podoba mi się prostota tego rozwiązania.
— Stefan Steinegger

1

Sprawdź https://github.com/blindpirate/annotation-magic , czyli bibliotekę, którą opracowałem, gdy miałem to samo pytanie.

@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()));
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.