Apakah ada sesuatu seperti Annotation Inheritance di java?

119

Saya menjelajahi anotasi dan sampai pada titik di mana beberapa anotasi tampaknya memiliki hierarki di antara anotasi tersebut.

Saya menggunakan anotasi untuk menghasilkan kode di latar belakang untuk Kartu. Ada berbagai jenis Kartu (dengan demikian kode dan anotasi berbeda) tetapi ada elemen tertentu yang umum di antara mereka seperti nama.

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

Dan ini akan menjadi Anotasi yang umum:

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

Dalam contoh di atas saya berharap Pindah ke mewarisi method3 tetapi saya mendapat peringatan yang mengatakan bahwa meluas tidak valid dengan anotasi. Saya mencoba agar Anotasi memperluas basis yang sama tetapi tidak berhasil. Apakah itu mungkin atau hanya masalah desain?

javydreamercsw.dll
sumber
3
Pewarisan anotasi tampaknya harus dimiliki untuk membuat DSL berdasarkan anotasi. Sayang sekali warisan anotasi tidak didukung.
Ceki
2
Saya setuju, sepertinya hal yang wajar untuk dilakukan. Terutama setelah memahami inheritance di Java, Anda mengharapkannya berlaku untuk semuanya.
javydreamercsw

Jawaban:

76

Sayangnya tidak ada. Rupanya ini ada hubungannya dengan program yang membaca anotasi di kelas tanpa memuatnya sepenuhnya. Lihat Mengapa tidak mungkin memperluas anotasi di Java?

Namun, tipe mewarisi anotasi superkelasnya jika anotasi tersebut adalah @Inherited.

Selain itu, kecuali Anda memerlukan metode tersebut untuk berinteraksi, Anda bisa menumpuk anotasi di kelas Anda:

@Move
@Page
public class myAwesomeClass {}

Apakah ada alasan yang tidak berhasil untuk Anda?

andronikus
sumber
1
Itulah yang saya pikirkan tetapi saya mencoba menyederhanakan banyak hal. Mungkin menerapkan yang Umum ke kelas abstrak akan berhasil ...
javydreamercsw
1
Ya, itu terlihat cukup pintar juga. Semoga berhasil!
andronikus
67

Anda dapat membuat anotasi anotasi Anda dengan anotasi dasar, bukan dengan pewarisan. Ini digunakan dalam kerangka Spring .

Memberi contoh

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

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

@Car
class Foo {
}

Anda kemudian dapat memeriksa apakah sebuah kelas dianotasi Vehiclemenggunakan Spring's AnnotationUtils :

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

Metode ini diimplementasikan sebagai:

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

AnnotationUtilsjuga berisi metode tambahan untuk mencari penjelasan tentang metode dan elemen beranotasi lainnya. Kelas Spring juga cukup kuat untuk mencari melalui metode bridged, proxy, dan kasus sudut lainnya, terutama yang ditemui di Spring.

Grygoriy Gonchar
sumber
13
Harap sertakan penjelasan tentang cara memproses anotasi tersebut.
Aleksandr Dubinsky
1
Anda dapat menggunakan Spring's AnnotationUtils.findAnnotation (..), lihat: docs.spring.io/spring/docs/current/javadoc-api/org/…
rgrebski
2
Saat anotasi A dianotasi dengan anotasi B lain, dan kami menganotasi kelas C dengan A, kelas C diperlakukan seolah-olah dianotasi dengan A dan B. Ini adalah perilaku spesifik framework Spring - AnnotationUtils.findAnnotation melakukan keajaiban di sini dan digunakan untuk melintasi untuk menemukan anotasi dari anotasi. Jadi jangan salah paham bahwa ini adalah perilaku default Java terkait penanganan anotasi.
qartal
Ini hanya mungkin jika anotasi yang ingin Anda buat memiliki target TYPEatau ANNOTATION_TYPE.
OrangeDog
7

Selain jawaban Grygoriys tentang anotasi anotasi.

Anda dapat memeriksa misalnya metode untuk memuat @Qualifieranotasi (atau anotasi dengan anotasi @Qualifier) oleh loop ini:

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

Apa yang pada dasarnya Anda lakukan, adalah menampilkan semua anotasi pada metode dan dari anotasi tersebut Anda mendapatkan jenisnya dan memeriksa jenis tersebut jika dianotasi dengan @Qualifier. Anotasi Anda harus Target.Annotation_type diaktifkan juga agar ini berfungsi.

filnat
sumber
Apa bedanya dengan jawaban Grygoriy?
Aleksandr Dubinsky
@ AleksandrDubinsky: Ini hanya implementasi yang jauh lebih sederhana tanpa menggunakan Spring. Itu tidak secara rekursif menemukan anotasi beranotasi, tapi saya rasa ini biasanya tidak diperlukan. Saya suka kesederhanaan solusi ini.
Stefan Steinegger
1

Lihat https://github.com/blindpirate/annotation-magic , yang merupakan perpustakaan yang saya kembangkan ketika saya memiliki pertanyaan yang sama.

@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()));
pengguna11949000
sumber