Có điều gì đó giống như Thừa kế chú thích trong java không?


119

Tôi đang khám phá các chú thích và đi đến một điểm mà một số chú thích dường như có hệ thống phân cấp giữa chúng.

Tôi đang sử dụng chú thích để tạo mã trong nền cho Thẻ. Có các loại Thẻ khác nhau (do đó mã và chú thích khác nhau) nhưng có một số yếu tố chung giữa chúng như tên.

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

Và đây sẽ là Chú thích chung:

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

Trong ví dụ trên, tôi mong đợi Move sẽ kế thừa method3 nhưng tôi nhận được cảnh báo nói rằng extension không hợp lệ với các chú thích. Tôi đã cố gắng để có Annotation mở rộng một cơ sở chung nhưng điều đó không hoạt động. Điều đó có khả thi không hay chỉ là một vấn đề thiết kế?


3
Kế thừa chú thích dường như là điều bắt buộc để tạo DSL dựa trên chú thích. Thật đáng tiếc khi kế thừa chú thích không được hỗ trợ.
Ceki

2
Tôi đồng ý, có vẻ như là một điều tự nhiên phải làm. Đặc biệt là sau khi hiểu kế thừa trên Java, bạn có thể mong đợi nó áp dụng cho mọi thứ.
javydreamercsw

Câu trả lời:


76

Tiếc là không có. Rõ ràng nó có liên quan gì đó đến các chương trình đọc chú thích trên một lớp mà không cần tải chúng. Xem Tại sao không thể mở rộng chú thích trong Java?

Tuy nhiên, các kiểu kế thừa các chú thích của lớp cha của chúng nếu các chú thích đó @Inherited.

Ngoài ra, trừ khi bạn cần những phương thức đó để tương tác, bạn chỉ có thể xếp chồng các chú thích lên lớp của mình:

@Move
@Page
public class myAwesomeClass {}

Có lý do nào đó không phù hợp với bạn không?


1
Đó là những gì tôi nghĩ nhưng tôi đang cố gắng đơn giản hóa mọi thứ. Có lẽ áp dụng chung một đến một lớp trừu tượng sẽ làm các trick ...
javydreamercsw

1
Yeah, trông cũng khá thông minh. Chúc may mắn!
andronikus

67

Bạn có thể chú thích chú thích của mình bằng chú thích cơ sở thay vì kế thừa. Điều này được sử dụng trong khuôn khổ Spring .

Để đưa ra một ví dụ

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

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

@Car
class Foo {
}

Sau đó, bạn có thể kiểm tra xem một lớp có được chú thích hay không bằng Vehiclecách sử dụng Spring's AnnotationUtils :

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

Phương pháp này được thực hiện như:

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

AnnotationUtilscũng chứa các phương thức bổ sung để tìm kiếm chú thích về các phương thức và các phần tử được chú thích khác. Lớp Spring cũng đủ mạnh để tìm kiếm thông qua các phương thức bắc cầu, proxy và các trường hợp góc khác, đặc biệt là những trường hợp gặp phải trong Spring.


13
Vui lòng bao gồm giải thích về cách xử lý các chú thích như vậy.
Aleksandr Dubinsky

1
Bạn có thể sử dụng Spring's AnnotationUtils.findAnnotation (..), xem: docs.spring.io/spring/docs/current/javadoc-api/org/…
rgrebski

2
Khi chú thích A được chú thích bằng chú thích khác B và chúng tôi chú thích lớp C với A, lớp C được coi như thể nó được chú thích với cả A và B. Đây là hành vi cụ thể của Spring framework - AnnotationUtils.findAnnotation thực hiện điều kỳ diệu ở đây và được sử dụng để duyệt qua để tìm các chú thích của một chú thích. Vì vậy, đừng hiểu lầm rằng đây là hành vi mặc định của Java liên quan đến việc xử lý chú thích.
qartal

Điều này chỉ có thể thực hiện được nếu các chú thích bạn muốn soạn có mục tiêu là TYPEhoặc ANNOTATION_TYPE.
OrangeDog

7

Ngoài Grygoriys câu trả lời của chú thích chú thích.

Bạn có thể kiểm tra ví dụ: các phương pháp để chứa một @Qualifierchú thích (hoặc một chú thích được chú thích bằng @Qualifier) bằng vòng lặp này:

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

Về cơ bản, những gì bạn đang làm là hiển thị tất cả các chú thích trên phương thức và trong số các chú thích đó, bạn nhận được các loại của chúng và kiểm tra các loại đó nếu chúng được chú thích bằng @Qualifier. Chú thích của bạn cũng cần được kích hoạt Target.Annotation_type để làm cho nó hoạt động.


Điều này khác với câu trả lời của Grygoriy như thế nào?
Aleksandr Dubinsky

@AleksandrDubinsky: Nó chỉ là một triển khai đơn giản hơn nhiều mà không cần sử dụng Spring. Nó không tìm thấy đệ quy các chú thích được chú thích, nhưng tôi đoán điều này thường không bắt buộc. Tôi thích sự đơn giản của giải pháp này.
Stefan Steinegger

1

Hãy xem https://github.com/blindpirate/annotation-magic , đây là một thư viện mà tôi đã phát triển khi tôi có cùng câu hỏi.

@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()));
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.