Showing posts with label Inheritance. Show all posts
Showing posts with label Inheritance. Show all posts

Thursday, October 19, 2017

Java Annotation, Reflection and Inheritance

Annotation

Annotation is most commonly used to store extra information in classes that could be used at runtime by frameworks.

An annotation is defined as meta-data type. attributes could be added to store specific values when the annotation is used.

Reflection

Frameworks use the Java reflection to retrieve annotations.

Inheritance

Annotation Subclass

It is not possible to extend an annotation to create a annotation type hierarchy. However a new annotation could be created to substitute an annotation with specific attribute values when it is used frequently. Or a new annotation could be used to represent multiple annotations if they are frequently used together.

such as:

@Category(value="Food")
public @interface FoodCategory {}

@Action1
@Action2
@Action3
public @interface ActionGroup1 {}


Annotation Type Declaration Inheritance

If the @Inherited annotation is used, the Annotation declaration is inherited in class hierarchy only. The annotation declaration on an interface is not inherited.

However, the Annotations declared on interfaces are able to be used by their sub types by using reflection to search its type hierarchy. If the interfaces are searched, it is going to be a tree search!

Here are some tests that demo the usages of get annotations in type hierarchies through reflection:



@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Inherited
public @interface MyAnnotation {
       String name();
       String value();
}

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@MyAnnotation(name = "usage", value = "extends")
public @interface MyAnnotationExt {
       String name();
       String value();
}

public class AnnotationUsages {

       @MyAnnotation(name = "name1", value = "value1")
       public void getAnnotation() {
             try {
                    Class<? extends AnnotationUsages> cls = this.getClass();
                    Method method = cls.getMethod("getAnnotation");
                    MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);
                    System.out.println(myAnnotation);
             } catch (SecurityException | NoSuchMethodException e) {
                    e.printStackTrace();
             }
       }

       @MyAnnotationExt(name = "name2", value = "value2")
       public void getAnnotationOfAnnotation() {
             try {
                    Class<? extends AnnotationUsages> cls = this.getClass();
                    Method mth = cls.getMethod("getAnnotationOfAnnotation");
                    Annotation[] annotations = mth.getAnnotations();
                    for (Annotation annotation : annotations) {
                           System.out.println(annotation.toString());
                    }
                    MyAnnotationExt myAnnotationExt = mth.getAnnotation(MyAnnotationExt.class);
                    annotations = myAnnotationExt.annotationType().getAnnotations();
                    for (Annotation annotation : annotations) {
                           System.out.println(annotation.toString());
                    }
             } catch (SecurityException | NoSuchMethodException e) {
                    e.printStackTrace();
             }
       }

       // Annotations over annotation

       @Test
       public void testMethodAnnotations() {
             AnnotationUsages usages = new AnnotationUsages();
             usages.getAnnotation();
             usages.getAnnotationOfAnnotation();
       }

       // Annotations over type hierarchy

       @Test
       public void testTypeAnnotations() {
             MyClass1 cls1 = new MyClass1();
             cls1.getTypeAnnotation();
             MyClass2 cls2 = new MyClass2();
             cls2.getTypeAnnotation();
             MyClass3 cls3 = new MyClass3();
             cls3.getTypeAnnotation();
             MyInterface myInterfaceInstance = new MyClass3();
             System.out.println("===MyInterface===");
             try {
                    Class<?> myInterface = myInterfaceInstance.getClass().getInterfaces()[0];
                    Annotation[] annotations = myInterface.getAnnotations();
                    for (Annotation annotation : annotations) {
                           System.out.println(annotation.toString());
                    }
             } catch (SecurityException e) {
                    e.printStackTrace();
             }
       }

       @MyAnnotation(name = "name1", value = "value1")
       @MyAnnotationExt(name = "name2", value = "value2")
       interface MyInterface {
             public void getTypeAnnotation();
       }

       @MyAnnotation(name = "name10", value = "value10")
       @MyAnnotationExt(name = "name20", value = "value20")
       public static class MyClass1 {
             public void getTypeAnnotation() {
                    try {
                           System.out.println("===MyClass1===");
                           Class<? extends MyClass1> cls = this.getClass();
                           Annotation[] annotations = cls.getAnnotations();
                           for (Annotation annotation : annotations) {
                                 System.out.println(annotation.toString());
                           }
                    } catch (SecurityException e) {
                           e.printStackTrace();
                    }
             }
       }

       public static class MyClass2 extends MyClass1 {
             @Override
             public void getTypeAnnotation() {
                    System.out.println("===MyClass2===");
                    try {
                           Class<? extends MyClass2> cls = this.getClass();
                           Annotation[] annotations = cls.getAnnotations();
                           for (Annotation annotation : annotations) {
                                 System.out.println(annotation.toString());
                           }
                    } catch (SecurityException e) {
                           e.printStackTrace();
                    }
             }
       }

       public static class MyClass3 implements MyInterface {
             @Override
             public void getTypeAnnotation() {
                    System.out.println("===MyClass3===");
                    try {
                           Class<? extends MyClass3> cls = this.getClass();
                           Annotation[] annotations = cls.getAnnotations();
                           for (Annotation annotation : annotations) {
                                 System.out.println(annotation.toString());
                           }
                    } catch (SecurityException e) {
                           e.printStackTrace();
                    }
             }
       }
}





 The results:


@com.americanexpress.ssae.sample.annotation.MyAnnotation(name=name1, value=value1)
@com.americanexpress.ssae.sample.annotation.MyAnnotationExt(name=name2, value=value2)
@java.lang.annotation.Retention(value=RUNTIME)
@java.lang.annotation.Target(value=[TYPE, METHOD, PARAMETER, FIELD, ANNOTATION_TYPE])
@com.americanexpress.ssae.sample.annotation.MyAnnotation(name=usage, value=extends)

===MyClass1===
@com.americanexpress.ssae.sample.annotation.MyAnnotation(name=name10, value=value10)
@com.americanexpress.ssae.sample.annotation.MyAnnotationExt(name=name20, value=value20)
===MyClass2===
@com.americanexpress.ssae.sample.annotation.MyAnnotation(name=name10, value=value10)
===MyClass3===
===MyInterface===
@com.americanexpress.ssae.sample.annotation.MyAnnotation(name=name1, value=value1)
@com.americanexpress.ssae.sample.annotation.MyAnnotationExt(name=name2, value=value2)