14 Java - Annotations

What is Annotation?

  • It is kind of adding META DATA to the java code.

  • Means, its usage is OPTIONAL.

  • We can use this meta data information at runtime and can add certain logic in our code if wanted.

  • How to Read Meta data information? Using Reflection

  • Annotations can be applied at anywhere like Classes, Methods, Interface, Fields, parameters etc.

Example:

Annotation (Denoted using @)

public class Eagle implements Bird{
    @Override
    public boolean fly() {
        return true;
    }
}

Types of Annotations

Pre-Defined: Annotations used on Java Code

@ Deprecated

  • Usage of Deprecated Class or Method or fields, shows you compile time WARNING.

  • Deprecation means, no further improvement is happening on this and use new alternative method or field instead.

  • Can be used over: Constructor, Field, Local Variable, Method, Package, Parameter, Type (class, interface, enum)

public interface Bird {
    public boolean fly();
}

@ Override

  • During Compile time, it will check that the method should be Overriden

  • And throws compile time error, if it do not match with parent method.

  • Can be used over: METHODS

@ SupressWarnings

  • It will tell compiler to IGNORE any compile time WARNING.

  • Use it safely, could led to Run time exception if any valid warning is IGNORED.

  • Can be used over: Field, Method, Parameter, Constructor, Local Variable, Type(Class or interface or enum)

public class Main {

    @SuppressWarnings("deprecation")
    public static void main(String[] args) {
        Mobile mobileObj = new Mobile();
        mobileObj.dummyMethod();
    }
}

Or

@SuppressWarnings("deprecation")
public class Main {

    public static void main(String[] args) {
        Mobile mobileObj = new Mobile();
        mobileObj.dummyMethod();
    }
}

Suppressing all types of Warnings

@SuppressWarnings("all")
public class Main {

    public static void main(String[] args) {
        Mobile mobileObj = new Mobile();
        mobileObj.dummyMethod();
    }
}

Suppressing warnings for unused methods

public class Main {

    public static void main(String[] args) {

    }

    @SuppressWarnings("unused")
    public void method1(){

    }
}

@ FunctionalInterface

  • Restrict Interface to have only 1 abstract method.

  • Throws compilation error, if more than 1 abstract method found.

  • Can be used over: Type (Class or interface, or enum)

@ SafeVarags

  • Used to supress "Heap pollution warning"

  • Used over methods and Constructors which has Variable Arguments as parameter.

  • Method should be either static or final (i.e methods which can not be overriden)

  • In Java 9, we can also use it on private methods too.

What is Heap Pollution?

Object of One Type (Example String), storing the reference of another type Object (Example Integer)

public class Log {
    public static void printLogValues(List<Integer>... logNumberList) {
        Object[] objectList = logNumberList;

        List<String> stringValueList = new ArrayList<>();
        stringValueList.add("Hello");
        objectList[0] = stringValueList;
    }
}

Fix

public class Log {
    @SafeVarargs
    public static void printLogValues(List<Integer>... logNumberList) {
        Object[] objectList = logNumberList;

        List<String> stringValueList = new ArrayList<>();
        stringValueList.add("Hello");
        objectList[0] = stringValueList;
    }
}

Pre-Defined: Annotations used over Another Annotations (META-ANNOTATIONS)

@ Target

  • This meta-annotations will restrict, where to use the annotation. Either at method or Constructor or Fields etc..
@Target(ElementType.METHOD)
public @interface Override {
}
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
public @interface SafeVarargs {}

Element Type:

  • TYPE

  • FIELD

  • METHOD

  • PARAMETER

  • CONSTRUCTOR

  • LOCAL_VARIABLE

  • ANNOTATION_TYPE

  • PACKAGE

  • TYPE_PARAMETER (Allow you to apply on generic types <T>)

  • TYPE_USE -Java 8 featur, allow you to use annotation at all places where Type you can declare(like List<@ annotation String>)

@ Retention

  • This meta-annotation tells, how Annotation will be stored in java.

  • RetentionPolicy.SOURCE : Annotations will be discarded by the compiler itself and it will not be recorded in .class file

  • RetentionPolicy.CLASS : Annotations will be recorded in .class file but will be ignore by JVM at run time.

  • RetentionPolicy.RUNTIME : Annotations will be recorded in .class file + available during run time. Usage of reflection can be done.

Example 1:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

public class Eagle implements Bird{
    @Override
    public boolean fly() {
        return true;
    }
}

Execute the command javac Bird.java Eagle.java to generate the class file

Eagle.class file

package learn.Annotations;

public class Eagle implements Bird {
    public Eagle() {
    }

    public boolean fly() {
        return true;
    }
}

Example 2:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
public @interface SafeVarargs {}

public class Log {
    @SafeVarargs
    public static void printLogValues(List<Integer>... logNumberList) {
        Object[] objectList = logNumberList;

        List<String> stringValueList = new ArrayList<>();
        stringValueList.add("Hello");
        objectList[0] = stringValueList;
    }
}

.class file

public class Log {
    public Log() {
    }

    @SafeVarargs
    public static void printLogValues(List<Integer>... var0) {
        ArrayList var2 = new ArrayList();
        var2.add("Hello");
        var0[0] = var2;
    }
}

Example 3:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyCustomAnnotationWithInherited {
}
@MyCustomAnnotationWithInherited
public class TestClass {
}
public class Main {

    public static void main(String[] args) {
        System.out.println(new TestClass().getClass().getAnnotation(MyCustomAnnotationWithInherited.class));
    }
}

/*
Output-----
@learn.Annotations.MyCustomAnnotationWithInherited()
*/

Example 4:

@Target(ElementType.TYPE)
public @interface MyCustomAnnotationWithInherited {
}
@MyCustomAnnotationWithInherited
public class TestClass {
}
public class Main {

    public static void main(String[] args) {
        System.out.println(new TestClass().getClass().getAnnotation(MyCustomAnnotationWithInherited.class));
    }
}

/*
Output-----
null
*/

@ Documented

  • By default, Annotations are ignored when Java Documentation is generated.

  • With this meta-Annotation even Annotations will come in Java Docs.

  • We can generate Java doc from tools in the IDE.

Not Documented

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

Documented

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
public @interface SafeVarargs {}
public class Log {
    @SafeVarargs
    public static void printLogValues(List<Integer>... logNumberList) {
        Object[] objectList = logNumberList;

        List<String> stringValueList = new ArrayList<>();
        stringValueList.add("Hello");
        objectList[0] = stringValueList;
    }
}

@ Inherited

  • By default, Annotations applied on parent class are not available to Child Classes.

  • But it is after this meta-annotation

  • This Meta-annotation has no effect, if annotation is used other than a class.

@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyCustomAnnotationWithInherited {
}
@MyCustomAnnotationWithInherited
public class ParentClass {
}
public class ChildClass extends ParentClass{
}
public class Main {

    public static void main(String[] args) {
        System.out.println(new ChildClass().getClass().getAnnotation(MyCustomAnnotationWithInherited.class));
    }
}

/*
Output ----
@learn.Annotations.MyCustomAnnotationWithInherited()
*/

Without Inherited Annotation

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyCustomAnnotationWithInherited {
}
public class Main {
    public static void main(String[] args) {
        System.out.println(new ChildClass().getClass().getAnnotation(MyCustomAnnotationWithInherited.class));
    }
}

/*
Output ----
null
*/

@ Repeatable

  • Allow us to use the same annotation more than 1 at same place.

  • We cannot do this before JAVA8

Issue

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Category {
    String name();
}

Fix - We need to use @ Repeatable Meta-annotation

@Repeatable(Categories.class)
public @interface Category {
    String name();
}
@Retention(RetentionPolicy.RUNTIME)
public @interface Categories {
    Category[] value();
}
@Category(name="Bird")
@Category(name="LivingThing")
@Category(name="carnivorous")
public class Eagle{
    public boolean fly() {
        return true;
    }
}

Application:

public class Main {

    public static void main(String[] args) {
        Category[] categoryAnnotationArray = new Eagle().getClass().getAnnotationsByType(Category.class);
        for (Category annotation: categoryAnnotationArray){
            System.out.println(annotation.name());
        }
    }
}
/*
Output ------
Bird
LivingThing
carnivorous
*/

User Defined or Custom Annotations

  • We can create our own ANNOTATION using keyword "@ interface"

Creating an Annotation with empty body

public @interface MyCustomAnnotation {
}
@MyCustomAnnotation
public class Eagle{
    public boolean fly() {
        return true;
    }
}

Creating an Annotation with method (its more like a field)

  • No parameter, no body.

  • Return type is restricted to Primitive, Class, String, enums, annotations and array of these types.

public @interface MyCustomAnnotation {
    String name();
}
@MyCustomAnnotation(name = "testing")
public class Eagle{
    public boolean fly() {
        return true;
    }
}

Creating an Annotation with an element with Default values

  • Default value cannot be null
public @interface MyCustomAnnotation {
    String name() default "hello";
}
@MyCustomAnnotation
public class Eagle {
    public boolean fly() {
        return true;
    }
}