Skip to content

Java Annotations#

Definition#

  • Annotations, a form of metadata, provide data about a program that is not part of the program itself. Annotations have no direct effect on the operation of the code they annotate. Annotation introduced from Java 5. Annotations used in the source codes will be compiled into bytecode and then reflection will be used to query the information metadata and take appropriate action. We can annotate classes (classes), methods (methods), variables (variables), packages (packages) and parameters (prameters) in Java.
  • There are 3 purposes for using Annotations:

    • Information for the compiler — Annotations can be used by the compiler to detect errors or suppress warnings.
    • Compile-time and deployment-time processing — Software tools can process annotation information to generate code, XML files, and so forth.
    • Runtime processing — Some annotations are available to be examined at runtime.
  • More information

  • The hierarchy of annotation in java 8 is showed as below.

 #zoom

  • Table of General Purpose Annotations in Java 8.
Annotation Description
@FunctionalInterface Declares an interface as a functional interface (one abstract method).
@Repeatable Allows an annotation to be applied more than once to the same element.
@Documented Indicates that an annotation should be included in the javadoc.
@Retention Specifies the retention policy of an annotation.
@Target Specifies the kinds of elements an annotation can be applied to.
@Inherited Indicates that an annotation is inherited by subclasses.
@Override Indicates that a method overrides a method in a superclass.
@Deprecated Marks a method or class as deprecated.
@SafeVarargs Suppresses unsafe operation warnings for varargs.
@SuppressWarnings Suppresses specified compiler warnings.
  • In the annotaiton @Retention we have some possible configuration values are:

    • SOURCE — indicates that this annotation is available only in the source code and ignored by the Compiler and JVM, and hence not available in runtime.
    • CLASS — indicates that this annotation is available to the Compiler but not JVM, and hence not available during runtime.
    • RUNTIME — indicates that the annotation is available to JVM, and hence can be used in runtime.
  • Also in the @Target we have some possible configuration values are:

    • ANNOTATION_TYPE — means that the annotation can be applied to other annotations.
    • CONSTRUCTOR — can be applied to a constructor.
    • FIELD — can be applied to a field or property.
    • LOCAL_VARIABLE — can be applied to a local variable.
    • METHOD— can be applied to a method.
    • PACKAGE— can be applied to a package declaration.
    • PARAMETER — can be applied to the parameters of a method.
    • TYPE — can be applied to ClassInterfaceAnnotation, or enum declaration.
    • PACKAGE— can be applied to package declaration.
    • TYPE_PARAMETER — can be applied to the type parameter declaration.
    • TYPE_USE — can be applied to any type
  • Table of Meta Annotations

Annotation Description
@Retention Specifies the retention policy of an annotation (e.g., runtime, class, source).
@Documented Indicates that an annotation should be included in the javadoc documentation.
@Target Specifies the kinds of elements an annotation can be applied to (e.g., methods, fields, types).
@Inherited Indicates that an annotation is inherited by subclasses.
@Repeatable Allows an annotation to be applied more than once to the same element.

Create A Custom Annotation.#

  • To create a custom annotation please use the keyword @interface as below.
CustomAnnotation.java
1
2
3
public @interface GroupMark {

}
  • Then we must define two mandatory attributes for the custom annotation which are @Target and @Retention as below.
CustomAnnotation.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
package com.java.core.annotation;  

import java.lang.annotation.ElementType;  
import java.lang.annotation.Retention;  
import java.lang.annotation.RetentionPolicy;  
import java.lang.annotation.Target;  

@Retention(RetentionPolicy.RUNTIME)  
@Target({ElementType.FIELD, ElementType.TYPE})  
public @interface GroupMark {

}
  • For example, to specify that the annotation applies to classes and fields, we need to add @Target({ElementType.FIELD, ElementType.TYPE}), which specifies that this annotation only applies to classes and fields, and @Retention(RetentionPolicy.RUNTIME), which specifies that this annotation must be available at runtime.

  • Next, we can add the fields to the custom annotation. In this case, we need groupName and usedInAnotherGroup.

GroupNameEnum
1
2
3
4
5
6
package com.java.core.annotation;

public enum GroupNameEnum {
    HUMAN,
    ANIMAL
}
CustomAnnotation.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package com.java.core.annotation;  

import java.lang.annotation.ElementType;  
import java.lang.annotation.Retention;  
import java.lang.annotation.RetentionPolicy;  
import java.lang.annotation.Target;  

@Retention(RetentionPolicy.RUNTIME)  
@Target({ElementType.FIELD, ElementType.TYPE})  
public @interface GroupMark {  

    GroupNameEnum groupName();  

    boolean usedInAnotherGroup() default false;  

}
  • Then now, Let's create classes which use the custom annotation above for class and field.
DogDto.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.java.core.annotation;

@GroupMark(groupName = GroupNameEnum.ANIMAL)
public class DogDto {

    private String name;
    private String food;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getFood() {
        return food;
    }

    public void setFood(String food) {
        this.food = food;
    }
}
PersonDto.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package com.java.core.annotation;

@GroupMark(groupName = GroupNameEnum.HUMAN)
public class PersonDto {

    private String firstName;
    private String lastName;
    private String age;
    private String address;

    @GroupMark(groupName = GroupNameEnum.ANIMAL, usedInAnotherGroup = true)
    private DogDto pet;

    public PersonDto() {
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public DogDto getPet() {
        return pet;
    }

    public void setPet(DogDto pet) {
        this.pet = pet;
    }
}
  • Now, let's create the main class and use Java Reflection to read the annotation GroupMark details at runtime as below.
AnnotationMain.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.java.core.annotation;

import java.lang.reflect.Field;
import java.text.MessageFormat;
import java.util.Objects;

public class AnnotationMain {

    private static final String PARENT_GROUP_MESSAGE = "Class: {0} has groupName: {1} and usedInAnotherGroup: {2}";
    private static final String CHILD_GROUP_MESSAGE = "Class: {0} contains child group {1} which has groupName: {2} and usedInAnotherGroup: {3}";


    public static void main(String[] args) throws IllegalAccessException {
        PersonDto personDto = new PersonDto();
        personDto.setFirstName("John");
        personDto.setLastName("Wick");
        personDto.setAge("29");
        personDto.setAddress("3/115 Binh Thuan");

        DogDto dogDto = new DogDto();
        dogDto.setName("Pig Bull");
        dogDto.setFood("meat");
        personDto.setPet(dogDto);

        printGroupInformation(personDto);
        printGroupInformation(dogDto);
    }

    private static void printGroupInformation(Object object) {
        if (Objects.isNull(object) || !object.getClass().isAnnotationPresent(GroupMark.class)) {
            return;
        }
        GroupMark groupMark = object.getClass().getAnnotation(GroupMark.class);
        System.out.println(MessageFormat.format(PARENT_GROUP_MESSAGE,
                object.getClass().getSimpleName(),
                groupMark.groupName(),
                groupMark.usedInAnotherGroup()));
        for (Field field : object.getClass().getDeclaredFields()) {
            GroupMark childGroupMark = field.getAnnotation(GroupMark.class);
            if (Objects.nonNull(childGroupMark)) {
                System.out.println(MessageFormat.format(CHILD_GROUP_MESSAGE,
                        object.getClass().getSimpleName(),
                        field.getType().getSimpleName(),
                        childGroupMark.groupName(),
                        childGroupMark.usedInAnotherGroup()));
            }
        }
    }

}
  • This would give the output as below.
1
2
3
4
5
Class: PersonDto has groupName: HUMAN and usedInAnotherGroup: false
Class: PersonDto contains child group DogDto which has groupName: ANIMAL and usedInAnotherGroup: true
Class: DogDto has groupName: ANIMAL and usedInAnotherGroup: false

Process finished with exit code 0

See Also#

References#