Java에 Annotation Inheritance와 같은 것이 있습니까?


119

주석을 탐색 중이며 일부 주석이 계층 구조를 갖는 것처럼 보이는 지점에 도달했습니다.

저는 주석을 사용하여 카드의 백그라운드에서 코드를 생성하고 있습니다. 카드 유형 (코드와 주석이 다름)이 다르지만 이름처럼 공통되는 특정 요소가 있습니다.

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

그리고 이것은 일반적인 주석이 될 것입니다.

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

위의 예에서 Move가 method3을 상속 할 것으로 예상하지만 extends가 주석에 유효하지 않다는 경고가 표시됩니다. Annotation이 공통 기반을 확장하도록 시도했지만 작동하지 않습니다. 그게 가능할까요 아니면 디자인 문제일까요?


3
어노테이션 상속은 어노테이션을 기반으로 DSL을 작성하는 데 꼭 필요한 것 같습니다. 주석 상속이 지원되지 않는 것이 유감입니다.
Ceki 2013-04-15

2
동의합니다. 자연스러운 일인 것 같습니다. 특히 Java에서의 상속을 이해 한 후에는 모든 것에 적용될 것으로 기대합니다.
javydreamercsw

답변:


76

불행하게도. 분명히 그것은 완전히로드하지 않고 클래스의 주석을 읽는 프로그램과 관련이 있습니다. Java에서 주석을 확장 할 수없는 이유는 무엇입니까?를 참조하십시오 .

그러나 유형은 해당 주석이 인 경우 수퍼 클래스의 주석을 상속합니다 @Inherited.

또한 상호 작용하는 데 이러한 메서드가 필요하지 않으면 클래스에 주석을 쌓을 수 있습니다.

@Move
@Page
public class myAwesomeClass {}

당신에게 효과가없는 이유가 있습니까?


1
그것이 내가 생각한 것이지만 나는 일을 단순화하려고 노력했습니다. 어쩌면 트릭을 할 것입니다 추상 클래스에 공통 일을 적용 ...
javydreamercsw

1
그래, 그것도 꽤 영리 해 보였다. 행운을 빕니다!
andronikus

67

상속 대신 기본 주석으로 주석을 달 수 있습니다. 이것은 Spring 프레임 워크에서 사용됩니다 .

예를 들어

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

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

@Car
class Foo {
}

그런 다음 Spring의 AnnotationUtilsVehicle사용하여 클래스에 주석이 추가되었는지 확인할 수 있습니다 .

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
이러한 주석을 처리하는 방법에 대한 설명을 포함하십시오.
Aleksandr Dubinsky

1
Spring의 AnnotationUtils.findAnnotation (..)을 사용할 수 있습니다. 참조 : docs.spring.io/spring/docs/current/javadoc-api/org/…
rgrebski

2
어노테이션 A에 다른 어노테이션 B로 주석을 달고 클래스 C에 A를 어노테이션하면 클래스 C는 A와 B 모두로 주석이 달린 것처럼 처리됩니다. 이것은 Spring 프레임 워크의 특정 동작입니다. AnnotationUtils.findAnnotation이 마법을 수행합니다. 여기에서 주석의 주석을 찾기 위해 위로 이동하는 데 사용됩니다. 따라서 이것이 주석 처리와 관련하여 Java의 기본 동작임을 오해하지 마십시오.
qartal

작성하려는 주석의 대상이 TYPE또는 인 경우에만 가능합니다 ANNOTATION_TYPE.
OrangeDog

7

주석 주석에 대한 Grygoriys 답변 외에도.

예를 @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을 활성화해야합니다.


이것이 Grygoriy의 대답과 어떻게 다른가요?
Aleksandr Dubinsky

@AleksandrDubinsky : Spring을 사용하지 않고 훨씬 더 간단한 구현입니다. 주석이 달린 주석이 달린 주석을 재귀 적으로 찾지는 않지만 일반적으로 필요하지 않은 것 같습니다. 이 솔루션의 단순함이 마음에 듭니다.
Stefan Steinegger

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()));
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.