Java'da Ek Açıklama Kalıtımı gibi bir şey var mı?


119

Ek açıklamaları araştırıyorum ve bazı ek açıklamaların aralarında bir hiyerarşi var gibi göründüğü bir noktaya geldim.

Kartlar için arka planda kod oluşturmak için ek açıklamalar kullanıyorum. Farklı Kart türleri vardır (dolayısıyla farklı kodlar ve açıklamalar) ancak aralarında bir ad gibi ortak olan belirli öğeler vardır.

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

Ve bu, ortak Ek Açıklama olacaktır:

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

Yukarıdaki örnekte, Move'un method3'ü devralmasını beklerdim, ancak extends'in ek açıklamalarla geçerli olmadığını belirten bir uyarı alıyorum. Bir Ek Açıklama ortak bir temeli genişletmeye çalışıyordum ama bu işe yaramıyor. Bu mümkün mü yoksa sadece bir tasarım sorunu mu?


3
Ek açıklama mirası, ek açıklamalara dayalı bir DSL oluşturmak için sahip olunması gereken bir şey gibi görünüyor. Ek açıklama mirasının desteklenmemesi çok üzücü.
Ceki

2
Katılıyorum, yapılması doğal bir şey gibi görünüyor. Özellikle Java'daki kalıtımı anladıktan sonra, her şeye uygulanmasını bekliyorsunuz.
javydreamercsw

Yanıtlar:


76

Ne yazık ki hayır. Görünüşe göre bir sınıftaki notları tamamen yüklemeden okuyan programlarla ilgisi var. Bkz Neden Java ek açıklamaları uzatmak mümkün değildir?

Bununla birlikte, türler, eğer bu ek açıklamalar öyleyse, üst sınıflarının ek açıklamalarını devralırlar @Inherited.

Ayrıca, etkileşim için bu yöntemlere ihtiyacınız olmadıkça, notları sınıfınızda istifleyebilirsiniz:

@Move
@Page
public class myAwesomeClass {}

Senin için işe yaramayacak bir sebep var mı?


1
Ben de öyle düşünmüştüm ama işleri basitleştirmeye çalışıyordum. Belki de Ortak olanı soyut bir sınıfa uygulamak işe yarayabilir ...
javydreamercsw

1
Evet, bu da oldukça zekice görünüyordu. İyi şanslar!
andronikus

67

Ek açıklamanıza miras yerine temel bir açıklama ekleyebilirsiniz. Bu, Spring çerçevesinde kullanılır .

Bir örnek vermek gerekirse

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

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

@Car
class Foo {
}

Daha sonra, Spring'in AnnotationUtils'iniVehicle kullanarak bir sınıfa not verilip verilmediğini kontrol edebilirsiniz :

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

Bu yöntem şu şekilde uygulanır:

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

AnnotationUtilsayrıca yöntemler ve diğer açıklamalı öğeler üzerindeki ek açıklamaları aramak için ek yöntemler içerir. Bahar sınıfı, özellikle Spring'de karşılaşılanlar olmak üzere, köprülü yöntemler, vekiller ve diğer önemli durumlarda arama yapacak kadar güçlüdür.


13
Lütfen bu tür ek açıklamaların nasıl işleneceğine ilişkin bir açıklama ekleyin.
Aleksandr Dubinsky

1
Spring's AnnotationUtils.findAnnotation (..) 'ı kullanabilirsiniz, bakınız: docs.spring.io/spring/docs/current/javadoc-api/org/…
rgrebski

2
A notuna başka bir B notu eklendiğinde ve C sınıfına A notu eklediğimizde, C sınıfı hem A hem de B ile açıklamalıymış gibi kabul edilir - Bu, Spring çerçevesinin özel davranışıdır - AnnotationUtils.findAnnotation sihri yapar. burada ve bir ek açıklamanın ek açıklamalarını bulmak için yukarı gitmek için kullanılır. Bu nedenle, Java'nın ek açıklama işlemeyle ilgili varsayılan davranışı olduğunu yanlış anlamayın.
qartal

Bu yalnızca oluşturmak istediğiniz ek açıklamaların TYPEveya hedefine sahip olması durumunda mümkündür ANNOTATION_TYPE.
OrangeDog

7

Ek açıklamalar Grygoriys'in cevabına ek olarak.

Örneğin , bu döngü @Qualifierile bir açıklama (veya açıklama eklenmiş bir açıklama @Qualifier) içeren yöntemleri kontrol edebilirsiniz :

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

Temelde yaptığınız şey, yöntemde mevcut olan tüm ek açıklamaları almak ve bu ek açıklamaların türlerini almak ve bu türlerin @ Niteleyici ile açıklamalı olup olmadığını kontrol etmektir. Bunun çalışması için ek açıklamanızın da Target.Annotation_type etkinleştirilmiş olması gerekir.


Bunun Grygoriy'nin cevabından farkı nedir?
Aleksandr Dubinsky

@AleksandrDubinsky: Spring kullanmadan çok daha basit bir uygulama. Yinelemeli olarak ek açıklamalı ek açıklamaları bulmaz, ancak sanırım bu genellikle gerekli değildir. Bu çözümün basitliğini seviyorum.
Stefan Steinegger

1

Check out https://github.com/blindpirate/annotation-magic aynı soru vardı geliştirilen bir kitaplık ben olan.

@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()));
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.