【问题标题】:Is there something like Annotation Inheritance in java?java中有没有类似注解继承的东西?
【发布时间】:2011-12-07 08:55:24
【问题描述】:

我正在探索注解并发现一些注解似乎在它们之间具有层次结构。

我正在使用注释在后台为卡片生成代码。有不同的卡片类型(因此不同的代码和注释),但它们之间有一些共同的元素,如名称。

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

这将是常见的注释:

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

在上面的示例中,我希望 Move 继承方法 3,但我收到一条警告,指出扩展对注释无效。我试图让一个注释扩展一个公共基础,但这不起作用。这甚至可能还是只是一个设计问题?

【问题讨论】:

  • 注解继承似乎是创建基于注解的 DSL 的必备条件。可惜不支持注解继承。
  • 我同意,这似乎是一件很自然的事情。尤其是在了解了 Java 的继承之后,你有点期待它适用于所有事物。

标签: java inheritance annotations


【解决方案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()));

【讨论】:

    【解决方案2】:

    您可以使用基本注释而不是继承来注释您的注释。这是used in Spring framework

    举个例子

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

    然后,您可以使用Spring's AnnotationUtils 来检查一个类是否被Vehicle 注释:

    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 中遇到的那些情况。

    【讨论】:

    • 请说明如何处理此类注释。
    • 可以使用Spring的AnnotationUtils.findAnnotation(..),见:docs.spring.io/spring/docs/current/javadoc-api/org/…
    • 当注解A被另一个注解B注解,而我们用A注解类C时,类C被视为同时注解了A和B。这是Spring框架的具体行为-- AnnotationUtils.findAnnotation 在这里发挥了作用,用于向上遍历以查找注释的注释。所以不要误会这是 Java 关于注解处理的默认行为。
    • 这只有在您希望编写的注释的目标是TYPEANNOTATION_TYPE 时才有可能。
    【解决方案3】:

    很遗憾,没有。显然,它与读取类上的注释而不一直加载它们的程序有关。见Why is it not possible to extend annotations in Java?

    但是,如果这些注解是@Inherited,则类型会继承其超类的注解。

    此外,除非您需要这些方法进行交互,否则您可以将注释堆叠在您的类上:

    @Move
    @Page
    public class myAwesomeClass {}
    

    有什么不适合你的原因吗?

    【讨论】:

    • 这就是我的想法,但我试图简化事情。也许将 Common 应用于抽象类就可以了...
    【解决方案4】:

    除了 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 的回答有何不同?
    • @AleksandrDubinsky:在不使用 Spring 的情况下,这只是一个更简单的实现。它不会递归地找到带注释的注释,但我想这通常不是必需的。我喜欢这个解决方案的简单性。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-01-20
    • 1970-01-01
    • 1970-01-01
    • 2011-02-26
    • 1970-01-01
    • 2020-03-11
    • 2020-11-10
    相关资源
    最近更新 更多