Is There Something Like Annotation Inheritance in Java

Is there something like Annotation Inheritance in java?

Unfortunately, no. Apparently it has something to do with programs that read the annotations on a class without loading them all the way. See Why is it not possible to extend annotations in Java?

However, types do inherit the annotations of their superclass if those annotations are @Inherited.

Also, unless you need those methods to interact, you could just stack the annotations on your class:

@Move
@Page
public class myAwesomeClass {}

Is there some reason that wouldn't work for you?

Is it possible for class to inherit the annotations of the super class

Annotation inheritance works basically the same way as inheritance of methods or fields.

Since you can access annotations only through reflection, there are two basic methods in Class:

  • getAnnotations() returns all annotations on the current class and its super classes
  • getDeclaredAnnotations() returns all annotations on the current class

The problem the article you linked talks about is that Method#getAnnotation(...) accesses declaredAnnotations() of the class the method has been defined in, which as stated above only returns the annotations defined in that class and not those of the super classes.

This means that if you override one of the methods annotated with @Transactional you'd have to add the annotation there (or if the frameworks also looks in the class annotation it should find the @Transactional declared on AbstractMaqraaService).

How to use @inherited annotation in Java?

Just that there is no misunderstanding: You do ask about java.lang.annotation.Inherited. This is a annotation for annotations.It means that subclasses of annotated classes are considered having the same annotation as their superclass.

Example

Consider the following 2 Annotations:

@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface InheritedAnnotationType {

}

and

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface UninheritedAnnotationType {

}

If three classes are annotated like this:

@UninheritedAnnotationType
class A {

}

@InheritedAnnotationType
class B extends A {

}

class C extends B {

}

running this code

System.out.println(new A().getClass().getAnnotation(InheritedAnnotationType.class));
System.out.println(new B().getClass().getAnnotation(InheritedAnnotationType.class));
System.out.println(new C().getClass().getAnnotation(InheritedAnnotationType.class));
System.out.println("_________________________________");
System.out.println(new A().getClass().getAnnotation(UninheritedAnnotationType.class));
System.out.println(new B().getClass().getAnnotation(UninheritedAnnotationType.class));
System.out.println(new C().getClass().getAnnotation(UninheritedAnnotationType.class));

will print a result similar to this (depending on the packages of the annotation):

null
@InheritedAnnotationType()
@InheritedAnnotationType()
_________________________________
@UninheritedAnnotationType()
null
null

As you can see UninheritedAnnotationType is not inherited but C inherits annotation InheritedAnnotationType from B.

I don't know what methods have to do with that.

Why java classes do not inherit annotations from implemented interfaces?

I'd say the reason is that otherwise a multiple-inheritance problem would occur.

Example:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD) @Inherited
public @interface Baz { String value(); }

public interface Foo{
@Baz("baz") void doStuff();
}

public interface Bar{
@Baz("phleem") void doStuff();
}

public class Flipp{
@Baz("flopp") public void doStuff(){}
}

public class MyClass extends Flipp implements Foo, Bar{}

If I do this:

MyClass.class.getMethod("doStuff").getAnnotation(Baz.class).value()

what's the result going to be? 'baz', 'phleem' or 'flopp'?


For this reason, annotations on interfaces are rarely useful.

Emulate annotation inheritance for interfaces and methods with AspectJ

The problem here is not AspectJ but the JVM. In Java, annotations on

  • interfaces,
  • methods or
  • other annotations

are never inherited by

  • implementing classes,
  • overriding methods or
  • classes using annotated annotations.

Annotation inheritance only works from classes to subclasses, but only if the annotation type used in the superclass bears the meta annotation @Inherited, see JDK JavaDoc.

AspectJ is a JVM language and thus works within the JVM's limitations. There is no general solution for this problem, but for specific interfaces or methods you wish to emulate annotation inheritance for, you can use a workaround like this:

package de.scrum_master.aspect;

import de.scrum_master.app.Marker;
import de.scrum_master.app.MyInterface;

/**
* It is a known JVM limitation that annotations are never inherited from interface
* to implementing class or from method to overriding method, see explanation in
* <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/annotation/Inherited.html">JDK API</a>.
* <p>
* Here is a little AspectJ trick which does it manually.
*
*/
public aspect MarkerAnnotationInheritor {
// Implementing classes should inherit marker annotation
declare @type: MyInterface+ : @Marker;
// Overriding methods 'two' should inherit marker annotation
declare @method : void MyInterface+.two() : @Marker;
}

Please note: With this aspect in place, you can remove the (literal) annotations from the interface and from the annotated method because AspectJ's ITD (inter-type definition) mechanics adds them back to the interface plus to all implementing/overriding classes/methods.

Now the console log when running the Application says:

execution(de.scrum_master.app.Application())
execution(void de.scrum_master.app.Application.two())

By the way, you could also embed the aspect right into the interface so as to have everything in one place. Just be careful to rename MyInterface.java to MyInterface.aj in order to help the AspectJ compiler to recognise that it has to do some work here.

package de.scrum_master.app;

public interface MyInterface {
void one();
void two();

// Cannot omit 'static' here due to https://bugs.eclipse.org/bugs/show_bug.cgi?id=571104
public static aspect MarkerAnnotationInheritor {
// Implementing classes should inherit marker annotation
declare @type: MyInterface+ : @Marker;
// Overriding methods 'two' should inherit marker annotation
declare @method : void MyInterface+.two() : @Marker;
}
}

Update 2021-02-11: Someone suggested an edit to the latter solution, saying that the aspect MarkerAnnotationInheritor nested inside interface MyInterface is implicitly public static, so the modifiers in the aspect declaration could be omitted. In principle this is true, because members (methods, nested classes) of interfaces are always public by default and a non-static inner class definition would not make sense inside an interface either (there is no instance to bind it to). I like to be explicit in my sample code, though, because not all Java developers might know these details.

Furthermore, currently the AspectJ compiler in version 1.9.6 throws an error if we omit static. I have just created AspectJ issue #571104 for this problem.

Check if Annotation is inherited

You can use getDeclaredAnnotation instead of getAnnotation:

public boolean isInherited(Class<?> clazz, Class<? extends Annotation> annotation) {
return clazz.isAnnotationPresent(annotation) && clazz.getDeclaredAnnotation(annotation) == null;
}

Inherit annotations from abstract class?

The answer is: no

Java annotations are not inherited unless the annotation type has the @Inherited meta-annotation on it: https://docs.oracle.com/javase/7/docs/api/java/lang/annotation/Inherited.html.

Spring's @Component annotation does not have @Inherited on it, so you will need to put the annotation on each component class. @Service, @Controller and @Repository neither.

Why is it not possible to extend annotations in Java?

About the reason why it wasn't designed that way you can find the answer in the JSR 175 Design FAQ, where it says:

Why don’t you support annotation subtyping (where one annotation type extends another)?

It complicates the annotation type
system, and makes it much more
difficult to write “Specific Tools”.

“Specific Tools” — Programs that query
known annotation types of arbitrary
external programs. Stub generators,
for example, fall into this category.
These programs will read annotated
classes without loading them into the
virtual machine, but will load
annotation interfaces.

So, yes I guess, the reason is it just KISS. Anyway, it seems this issue (along with many others) are being looked into as part of JSR 308, and you can even find an alternative compiler with this functionality already developed by Mathias Ricken.



Related Topics



Leave a reply



Submit