Easy Tutorial
❮ Const Char C Assert ❯

Java Annotations (Annotation)

Category Programming Technology

In Java, classes, methods, variables, parameters, and packages can all be annotated. Unlike Javadoc, Java annotations can be retrieved via reflection. When the compiler generates the class file, annotations can be embedded into the bytecode. The Java Virtual Machine can retain annotation content, which can be accessed at runtime. It also supports custom Java annotations.

There are many articles online about Java Annotations that can be overwhelming. Java Annotations are actually quite simple, but the lack of clear explanations can make them more confusing.

I have organized my understanding of Annotations based on my own thought process. The key to understanding Annotations is to grasp their syntax and usage, which I have explained in detail. After understanding the syntax and usage of Annotations, looking at the framework diagram may give you a deeper insight. That's enough introduction, let's start explaining Annotations. If you find any errors or shortcomings in the article, please point them out!

Built-in Annotations

Java defines a set of annotations, with 7 in total, 3 in java.lang and the remaining 4 in java.lang.annotation.

Annotations that apply to code are:

Annotations that apply to other annotations (or meta-annotations) are:

Starting from Java 7, three additional annotations were added:


1. Annotation Architecture

From this, we can see:

(01) Each Annotation is associated with 1 RetentionPolicy.

(02) Each Annotation is associated with 1 to n ElementTypes.

This can be understood as: For each Annotation object, there can be several ElementType attributes.

(03) There are many implementation classes for Annotation, including Deprecated, Documented, Inherited, Override, etc.

Each implementation class of Annotation is "associated with 1 RetentionPolicy" and "associated with 1 to n ElementTypes."

Below, I will first introduce the left half of the framework diagram (as shown below), namely Annotation, RetentionPolicy, ElementType; then I will give examples of the implementation classes of Annotation.


2. Components of Annotation

In the composition of Java Annotations, there are three very important main classes. They are:

Annotation.java

package java.lang.annotation;
public interface Annotation {

    boolean equals(Object obj);

    int hashCode();

    String toString();

    Class<? extends Annotation> annotationType();
}

ElementType.java

package java.lang.annotation;

public enum ElementType {
    TYPE,               /* Class, interface (including annotation type), or enum declaration */

    FIELD,              /* Field declaration (including enum constants) */

    METHOD,             /* Method declaration */

    PARAMETER,          /* Parameter declaration */

    CONSTRUCTOR,        /* Constructor declaration */

    LOCAL_VARIABLE,     /* Local variable declaration */

    ANNOTATION_TYPE,    /* Annotation type declaration */

    PACKAGE             /* Package declaration */
}

RetentionPolicy.java

package java.lang.annotation;
public enum RetentionPolicy {
    SOURCE,             /* Annotation information exists only during compiler processing and is lost after processing */

    CLASS,              /* Compiler stores Annotation in the class's .class file. Default behavior */

    RUNTIME             /* Compiler stores Annotation in the class file and it can be read by the JVM */
}

Explanation:

(01) Annotation is an interface.

"Each Annotation" is associated with "1 RetentionPolicy" and "1 to n ElementTypes." It can be understood that each Annotation object has a unique RetentionPolicy attribute, and 1 to n ElementType attributes.

(02) ElementType is an Enum type, used to specify the type of Annotation.

"Each Annotation" is associated with "1 to n ElementTypes." When an Annotation is associated with a certain ElementType, it means that the Annotation has a certain use. For example, if an Annotation object is of METHOD type, then the Annotation can only be used to decorate methods.

(03) RetentionPolicy is an Enum type, used to specify the strategy of Annotation. In other words, Annotations with different RetentionPolicy types have different scopes.

"Each Annotation" is associated with "1 RetentionPolicy."

At this point, just remember that "each Annotation" is associated with "1 RetentionPolicy" and "1 to n ElementTypes." After learning the following content, looking back at these points will be easier to understand.


3. Java's Built-in Annotations

After understanding the functions of the above three classes, we can now explain the syntax definition of the Annotation implementation class.

1) General Definition of Annotation

@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation1 {
}

Explanation:

The above defines an Annotation named MyAnnotation1. After defining MyAnnotation1, we can use "@MyAnnotation1" in our code. The other elements, @Documented, @Target, @Retention, @interface, are all used to modify MyAnnotation1. Let's explain their meanings one by one:

(01) @interface

Using @interface to define an annotation means that it implements the java.lang.annotation.Annotation interface, that is, the annotation is an Annotation.

Note: This is different from the usual way of implementing interfaces with implemented. The implementation details of the Annotation interface are completed by the compiler. After defining an annotation with @interface, the annotation cannot inherit other annotations or interfaces.

(02) @Documented

By default, the Annotation of classes and methods does not appear in the javadoc. If @Documented is used to modify the Annotation, it means that it can appear in the javadoc.

When defining an Annotation, @Documented can be optional; if not defined, the Annotation will not appear in the javadoc.

(03) @Target(ElementType.TYPE)

As we mentioned earlier, ElementType is the type attribute of Annotation. The function of @Target is to specify the type attribute of Annotation.

@Target(ElementType.TYPE) means that the type of the specified Annotation is ElementType.TYPE. This means that MyAnnotation1 is an annotation used to decorate "class, interface (including annotation type), or enum declaration".

When defining an Annotation, @Target can be optional. If there is @Target, the Annotation can only be used in the specified place; if there is no @Target, the Annotation can be used anywhere.

(04) @Retention(RetentionPolicy.RUNTIME)

As we mentioned earlier, RetentionPolicy is the policy attribute of Annotation, and the function of @Retention is to specify the policy attribute of Annotation.

@Retention(RetentionPolicy.RUNTIME) means that the specified Annotation's policy is RetentionPolicy.RUNTIME. This means that the compiler will retain the Annotation information in the .class file and it can be read by the virtual machine.

When defining an Annotation, @Retention can be optional. If there is no @Retention, the default is RetentionPolicy.CLASS.

2) Java's Built-in Annotations

After understanding the above three classes, we can now explain the syntax definition of the Annotation implementation class. Through the examples above, we can understand that: @interface is used to declare an Annotation, @Documented indicates whether the Annotation will appear in javadoc, @Target specifies the type of the Annotation, and @Retention specifies the strategy of the Annotation.

After understanding this, it becomes easy to comprehend the implementation classes of the built-in Annotations in Java, which are on the right side of the Annotation architecture diagram. As shown below:

Commonly Used Annotations in Java:

@Deprecated  -- The content marked with @Deprecated is no longer recommended for use.
@Override    -- @Override can only mark methods, indicating that the method overrides a method from the superclass.
@Documented  -- The content marked with @Documented can appear in javadoc.
@Inherited   -- @Inherited can only be used to mark "Annotation types," and the Annotation it marks is inheritable.
@Retention   -- @Retention can only be used to mark "Annotation types," and it specifies the RetentionPolicy attribute of the Annotation.
@Target      -- @Target can only be used to mark "Annotation types," and it specifies the ElementType attribute of the Annotation.
@SuppressWarnings -- The warnings generated by the content marked with @SuppressWarnings will be silenced by the compiler.

Since "@Deprecated and @Override" are similar, and "@Documented, @Inherited, @Retention, @Target" are similar; below, we will only explain the @Deprecated, @Inherited, and @SuppressWarnings Annotations.

2.1) @Deprecated

The definition of @Deprecated is as follows:

@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Deprecated {
}

Explanation:

For example, if a method is marked with @Deprecated, it is no longer recommended for use. If a developer attempts to use or override a method marked with @Deprecated, the compiler will provide corresponding warning messages. An example is as follows:

DeprecatedTest.java

import java.util.Date;
import java.util.Calendar;

public class DeprecatedTest {
    // @Deprecated marks getString1(), indicating it is a method not recommended for use
    @Deprecated
    private static void getString1(){
        System.out.println("Deprecated Method");
    }

    private static void getString2(){
        System.out.println("Normal Method");
    }

    // Date is a date/time class. Java no longer recommends using this class
    private static void testDate() {
        Date date = new Date(113, 8, 25);
        System.out.println(date.getYear());
    }
    // Calendar is a date/time class. Java recommends using Calendar instead of Date to represent "date/time"
    private static void testCalendar() {
        Calendar cal = Calendar.getInstance();
        System.out.println(cal.get(Calendar.YEAR));
    }

    public static void main(String[] args) {
        getString1(); 
        getString2();
        testDate(); 
        testCalendar();
    }
}

Explanation:

The above is a screenshot from Eclipse, comparing "getString1() and getString2()" as well as "testDate() and testCalendar()".

(01) getString1() is marked with @Deprecated, meaning it is not recommended for use; therefore, both its definition and invocation have a strikethrough line. This line is Eclipse's treatment for @Deprecated methods.

getString2() is not marked with @Deprecated, and its display is normal.

(02) testDate() calls methods related to Date, and Java no longer recommends using Date for date/time operations. Therefore, when calling Date's API, a warning message is generated, as shown in the figure as warnings.

testCalendar() calls Calendar's API for date/time operations, and Java recommends using Calendar instead of Date. Therefore, operating Calendar does not produce a warning.

2.2) @Inherited

The definition of @Inherited is as follows:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}

Explanation:

For example, if MyAnnotation is marked with @Inherited, then Base will have the "MyAnnotation Annotation"; now, Sub inherits from Base, and since MyAnnotation is @Inherited (inheritable), Sub will also "have the MyAnnotation Annotation."

Example of using @Inherited:

InheritableSon.java

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

/**
 * Custom Annotation.
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@interface Inheritable
{
}

@Inheritable
class InheritableFather
{
    public InheritableFather() {
        // Check if InheritableFather has the Inheritable Annotation
        System.out.println("InheritableFather:"+InheritableFather.class.isAnnotationPresent(Inheritable.class));
    }
}

/**
 * InheritableSon class simply inherits from InheritableFather,
 */
public class InheritableSon extends InheritableFather
{
    public InheritableSon() {
        super();    // Call the constructor of the parent class
        // Check if InheritableSon has the Inheritable Annotation
        System.out.println("InheritableSon:"+InheritableSon.class.isAnnotationPresent(Inheritable.class));
    }

    public static void main(String[] args)
    {
        InheritableSon is = new InheritableSon();
    }
}

Execution result:

InheritableFather:true
InheritableSon:true

Now, let's modify InheritableSon.java: comment out the "@Inherited annotation" of "Inheritable."

InheritableSon.java

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

/**
 * Custom Annotation.
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
//@Inherited
@interface Inheritable
{
}

@Inheritable
class InheritableFather
{
    public InheritableFather() {
        // Check if InheritableFather has the Inheritable Annotation
        System.out.println("InheritableFather:"+InheritableFather.class.isAnnotationPresent(Inheritable.class));
    }
}

/**
 * InheritableSon class simply inherits from InheritableFather,
 */
public class InheritableSon extends InheritableFather
{
    public InheritableSon() {
        super();    // Call the constructor of the parent class
        // Check if InheritableSon has the Inheritable Annotation
        System.out.println("InheritableSon:"+InheritableSon.class.isAnnotationPresent(Inheritable.class));
    }

    public static void main(String[] args)
    {
        InheritableSon is = new InheritableSon();
    }
}

Execution result:

InheritableFather:true
InheritableSon:false
// Check if InheritableBase has the Inheritable Annotation
System.out.println("InheritableFather:" + InheritableFather.class.isAnnotationPresent(Inheritable.class));
}
}

/**
 * InheritableSon class simply inherits from InheritableFather,
 */
public class InheritableSon extends InheritableFather {
    public InheritableSon() {
        super(); // Call the constructor of the parent class
        // Check if InheritableSon class has the Inheritable Annotation
        System.out.println("InheritableSon:" + InheritableSon.class.isAnnotationPresent(Inheritable.class));
    }

    public static void main(String[] args) {
        InheritableSon is = new InheritableSon();
    }
}

Running result:

InheritableFather:true
InheritableSon:false

Comparing the two results above, we find that: When the Inheritable annotation is marked with @Inherited, it has inheritance. Otherwise, it does not have inheritance.

2.3) @SuppressWarnings

The definition of @SuppressWarnings is as follows:

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    String[] value();
}

Explanation:

(01) @interface -- It is used to modify SuppressWarnings, meaning that SuppressWarnings implements the java.lang.annotation.Annotation interface; that is, SuppressWarnings is an annotation.

(02) @Retention(RetentionPolicy.SOURCE) -- It specifies that the strategy for SuppressWarnings is RetentionPolicy.SOURCE. This means that SuppressWarnings information is only valid during compiler processing, and after the compiler processes it, SuppressWarnings loses its effect.

(03) @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) -- It specifies that the types of SuppressWarnings include TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE.

(04) String[] value(); means SuppressWarnings can specify parameters.

(05) The role of SuppressWarnings is to keep the compiler silent about certain warnings for the content it annotates. For example, "@SuppressWarnings(value={"deprecation", "unchecked"})" means keeping the compiler silent about "deprecation warnings" and "unchecked conversion warnings" for the annotated content. Example as follows:

SuppressWarningTest.java

import java.util.Date;

public class SuppressWarningTest {

    //@SuppressWarnings(value={"deprecation"})
    public static void doSomething(){
        Date date = new Date(113, 8, 26);
        System.out.println(date);
    }

    public static void main(String[] args) {
        doSomething();
    }
}

Explanation:

(01) In the left diagram, @SuppressWarnings(value={"deprecation"}) is not used, and Date is a class that Java no longer recommends using. Therefore, calling the Date API will generate a warning. In the right diagram, @SuppressWarnings(value={"deprecation"}) is used. Therefore, the compiler remains silent about the warning generated by calling the Date API.

Supplement: Common keywords for SuppressWarnings

deprecation  -- Warning when using deprecated classes or methods
unchecked    -- Warning when performing unchecked conversions, such as using collections without specifying the type with Generics.
fallthrough  -- Warning when a Switch block goes directly to the next case without Break.
path         -- Warning when there are non-existent paths in class paths, source file paths, etc.
serial       -- Warning when the serialVersionUID definition is missing in serializable classes.
finally      -- Warning when any finally clause does not complete normally.
all          -- Warning for all of the above situations.

4. The Role of Annotation

Annotation is a helper class widely used in tool frameworks such as Junit, Struts, and Spring.

The common uses of Annotation in programming include:

1) Compile-time checks

Annotation has the role of "allowing the compiler to perform compile-time checks".

For example, @SuppressWarnings, @Deprecated, and @Override all have compile-time check functions.

(01) Regarding @SuppressWarnings and @Deprecated, they have been detailed in "Section 3". No further examples are provided here.

(02) If a method is marked with @Override, it means that the method is intended to override a method with the same name in the parent class. If a method is marked with @Override but there is no corresponding method with the same name in the parent class, the compiler will report an error. Example as follows:

OverrideTest.java

public class OverrideTest {

    /**
     * toString() is defined in java.lang.Object;
     * Therefore, using @Override here is correct.
     */
    @Override
    public String toString(){
        return "Override toString";
    }

    /**
     * getString() is not defined in any parent class of OverrideTest;
     * However, it is marked with @Override, so a compile error will occur!
     */
    @Override
    public String getString(){
        return "get toString";
    }

    public static void main(String[] args) {
    }
}

The above is a screenshot of the program in eclipse. We can see that the "getString()" function will report an error. This is because "getString() is marked with @Override, but there is no getString1() method defined in any parent class of OverrideTest".

"Commenting out the @Override above getString()" will resolve this error.

2) Using Annotation in Reflection

In reflection's Class, Method, Field, and other functions, there are many interfaces related to Annotation.

This also means that we can parse and use Annotation in reflection.

AnnotationTest.java

import java.lang.annotation.Annotation;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Inherited;
import java.lang.reflect.Method;

/**
 * Example of using Annotation in reflection functions
 */
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {
    String[] value() default "unknown";
}

/**
 * Person class. It uses MyAnnotation annotation.
 */
class Person {

    /**
     * The empty() method is annotated with both "@Deprecated" and "@MyAnnotation(value={"a","b"})"
     * (01) @Deprecated means the empty() method is no longer recommended for use
     * (02) @MyAnnotation means the value of MyAnnotation for the empty() method is the default value "unknown"
     */
    @MyAnnotation
    @Deprecated
    public void empty(){
        System.out.println("\nempty");
    }

    /**
     * The somebody() method is annotated with @MyAnnotation(value={"girl","boy"})
     * @MyAnnotation(value={"girl","boy"}) means the value of MyAnnotation is {"girl","boy"}
     */
    @MyAnnotation(value={"girl","boy"})
    public void somebody(String name, int age){
        System.out.println("\nsomebody: "+name+", "+age);
    }
}

public class AnnotationTest {

    public static void main(String[] args) throws Exception {

        // Create a new Person
        Person person = new Person();
        // Get Class object
        Class<Person> c = Person.class;
        // Get the empty() method
        Method mEmpty = c.getMethod("empty", new Class[]{});
        // Execute the empty() method
        mEmpty.invoke(person, new Object[]{});
        // Get the somebody() method
        Method mSomebody = c.getMethod("somebody", new Class[]{String.class, int.class});
        // Execute the somebody() method
        mSomebody.invoke(person, new Object[]{"lily", 18});

        // Traverse all annotations of the empty() method
        traverseAnnotation(mEmpty);
        // Traverse all annotations of the somebody() method
        traverseAnnotation(mSomebody);
    }

    public static void traverseAnnotation(Method method) {
        // Check if the method has MyAnnotation annotation
        if(method.isAnnotationPresent(MyAnnotation.class)){
            // Get MyAnnotation annotation
            MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);
            // Get value of MyAnnotation
            String[] values = myAnnotation.value();
            for (String str: values)
                System.out.printf(str+", ");
            System.out.println();
        }
        // Get all annotations of the method
        Annotation[] annotations = method.getAnnotations();
        for(Annotation annotation : annotations){
            System.out.println(annotation);
        }
    }
}
Person person = new Person();
// Get the Class instance of Person
Class<Person> c = Person.class;
// Get the Method instance of the somebody() method
Method mSomebody = c.getMethod("somebody", new Class[]{String.class, int.class});
// Execute the method
mSomebody.invoke(person, new Object[]{"lily", 18});
iteratorAnnotations(mSomebody);

// Get the Method instance of the empty() method
Method mEmpty = c.getMethod("empty", new Class[]{});
// Execute the method
mEmpty.invoke(person, new Object[]{});
iteratorAnnotations(mEmpty);
}

public static void iteratorAnnotations(Method method) {

// Check if the somebody() method contains the MyAnnotation annotation
if(method.isAnnotationPresent(MyAnnotation.class)){
// Get the MyAnnotation annotation instance of the method
MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);
// Get the values of myAnnotation and print them
String[] values = myAnnotation.value();
for (String str:values)
System.out.printf(str+", ");
System.out.println();
}

// Get all annotations on the method and print them
Annotation[] annotations = method.getAnnotations();
for(Annotation annotation : annotations){
System.out.println(annotation);
}
}
}

3) Generate Help Documentation Based on Annotation

By adding the @Documented tag to the Annotation, the Annotation can appear in the javadoc.

4) Help to Review Code

Through annotations like @Override and @Deprecated, we can easily understand the general structure of the program.

Additionally, we can implement some functionalities through custom annotations.

>

Original Source: https://www.cnblogs.com/skywang12345/p/3344137.html

** Click to Share Notes

Cancel

-

-

- ```

❮ Const Char C Assert ❯