banner
jzman

jzman

Coding、思考、自觉。
github

Java Series of Annotations

Java annotations, also known as Java annotations, metadata, are a special syntax introduced in Java 1.5. Annotations can be used to mark classes, methods, properties, parameters, packages, etc. in Java. These metadata can be accessed through the reflection principle. The use of annotations does not affect the normal operation of the program, but only affects auxiliary tools such as compiler warnings. The main content is as follows:

  1. Annotation functionality
  2. Define annotations
  3. Built-in annotations
  4. Meta-annotations
  5. Type annotations

Annotation functionality#

  • The compiler can use annotations to detect errors and suppress warnings.
  • Annotations can be used to generate specific code, such as using annotations to simplify findViewById in ButterKnife.
  • Some annotations can be checked and manipulated at runtime.

Define annotations#

The definition of annotations uses @interface as the keyword, which actually means that it automatically inherits the java.lang.annotation.Annotation interface. The definition format is as follows:

@Meta-annotation
public @interface AnnotationName{
    // Configuration parameters (parameter type parameter name())
    String name() default "hello";
}

The types in the configuration parameters include basic types, String, class, enumerations, and arrays of related types. The default value of the configuration parameter can be set using default. The definition of an annotation is as follows:

@Target(value = {ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface TestDefineAnnotation {
    String[] name() default "test";
}

Built-in annotations#

  1. @Override
  2. @Deprecated
  3. @SuppressWarnings

The declarations of the above three built-in annotations are as follows:

// Indicates that the current method will override the method in the superclass, and format check will be performed at compile time
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
// Indicates that a class or method is no longer recommended for use, but can still be used
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    String[] value();
}
// Indicates the suppression of inappropriate compiler warning messages
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}

According to the declarations of the above three annotations, @SuppressWarnings defines an array, which represents the specific targets that can be used on SuppressWarnings, and commonly used values are as follows:

  • deprecation: warning when using deprecated classes or methods
  • unused: warning when there are unused variables
  • unchecked: warning when performing unchecked conversions
  • fallthrough: warning when a switch program block directly goes to the next case without break
  • path: warning when there are non-existent paths in class paths, source file paths, etc.
  • serial: warning when a serializable class lacks serialVersionUID definition
  • finally: warning when any finally clause cannot be completed normally
  • all: warnings about all the above cases

Here is an example, as follows:

public void test() {
    long date = Date.parse("2018-04-22");
}

If you use IDEs such as Eclipse, two warnings will appear. One is that an outdated API is used, and the other is that the variable date is not used. The screenshot of the warnings is as follows:

image

Of course, the IDE will prompt whether to add SuppressWarnings to suppress these warnings. As mentioned earlier, the declaration of the @SuppressWarnings annotation requires configuring parameters. This parameter is an array, and the array name is value. This name can be omitted, as shown below:

// Not omitted
public void test2() {
    @SuppressWarnings(value= {"deprecation", "unused"})
    long date = Date.parse("2018-04-22");
}
// Omitted
public void test2() {
    @SuppressWarnings({"deprecation", "unused"})
    long date = Date.parse("2018-04-22");
}

Here is a screenshot to illustrate the effect of using @SuppressWarnings, as follows:

image

If you only want to suppress one warning, you can write it like this:

// First method
public void test2() {
    @SuppressWarnings(value = {"deprecation"})
    long date = Date.parse("2018-04-22");
    System.out.println(date);
}
// Second method
public void test2() {
    @SuppressWarnings({"deprecation"})
    long date = Date.parse("2018-04-22");
    System.out.println(date);
}

Note: If the configuration parameter name in the definition of the annotation is value, then the value can be omitted when configuring the annotation. On the contrary, if another name is used, the first method must be used to specify the configuration parameter name.

Other annotations are similar to @SuppressWarnings. @Override and @Deprecated can be used directly based on their declaration annotations. There is no need to specify specific targets. When declaring these annotations, @Documented, @Retention, @Target, etc. are used. These special annotations used to annotate other annotations are called meta-annotations. Please see the following text for details.

Meta-annotations#

  1. @Target
  2. @Retention
  3. @Documented
  4. @Inherited

@Target#

@Target is used to describe the scope of annotation usage. Its declaration is as follows:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    ElementType[] value();
}

According to the declaration of @Target, when using @Target annotation, specific Java members must be specified, that is, which position the annotation should be used in. The specific positions are defined in the ElementType enumeration, as follows:

public enum ElementType {
    TYPE,           // Class, interface, annotation, enumeration
    FIELD,          // Property (including enumeration constants)
    METHOD,         // Method
    PARAMETER,      // Parameter
    CONSTRUCTOR,    // Constructor
    LOCAL_VARIABLE, // Local variable
    ANNOTATION_TYPE,// Annotation
    PACKAGE,        // Package

    /**
     * Type annotation
     * @since 1.8
     */
    TYPE_PARAMETER,
    TYPE_USE
}

@Retention#

@Retention indicates at what level the information of the annotation is saved. Its declaration is as follows:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
    RetentionPolicy value();
}

According to the declaration of @Retention, when using @Retention, the retention policy (RetentionPolicy) must be specified, and the specific values are as follows:

public enum RetentionPolicy {
    SOURCE,  // Discarded during compilation, only exists in source code
    CLASS,   // Default policy, discarded at runtime, only exists in class files
    RUNTIME  // The annotation information is recorded in the class file during compilation and still exists at runtime. The annotation information can be obtained through reflection.
}

@Documented and @Inherited do not have configuration parameters. They are marker annotations. @Documented indicates that the annotation will be displayed in the user documentation, and @Inherited indicates that the annotation is only effective when used on classes and will be inherited by subclasses.

Type Annotations#

As mentioned in the explanation of meta-annotations, starting from Java 8, type annotations were added. If this annotation is used in @Target, it means that the annotation can be used anywhere corresponding to it. For example, if TYPE_PARAMETER is specified in @Target, the annotation can be used at the declaration of custom types. If TYPE_USE is specified in @Target, the annotation can be added before any type. This is mainly to facilitate Java developers to use type annotations and related plugins (Checker Framework) to check runtime exceptions during compilation.

Next, define annotations that specify TYPE_PARAMETER and TYPE_USE, as follows:

// 1. TYPE_PARAMETER
@Target(value = {ElementType.TYPE_PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface TypeParameterAnnotation {
    String value();
}
// 2. TYPE_USE
@Target(value = ElementType.TYPE_USE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TypeUseAnnotation {
}

Then, use these two annotations in the following example, as follows:

/**
 * Test annotation
 * @author jzman
 */
public class TestAnnotation {

    //...
    
    /**
     * ElementType.TYPE_PARAMETER
     * Used in the declaration of custom types, such as the annotation @TypeParameterAnnotation
     * @param <T>
     */
    static class TypeAnnotationA<@TypeParameterAnnotation(value="hello") T>{
        /**
         * ElementType.TYPE_USE
         * Can be used before any type (including TYPE_PARAMETER)
         */
        // Create an instance
        MyType myType = new @TypeUseAnnotation MyType();
        // Object type
        Object obj = (@TypeUseAnnotation Object) myType;
        // Generics
        ArrayList<@TypeUseAnnotation T> list = new ArrayList<>();
        // Type in parameters
        public String testA(@TypeUseAnnotation String test) {
            return "Hello"+test;
        }
        // Enumeration
        public void testB(@TypeUseAnnotation Color color) {
            //...
        }
        
        enum Color{
            RED, GREEN, BLUE
        }
    }
    
    static class MyType{}    
}

In fact, the syntax of annotations is relatively simple, and it is not helpful to define annotations alone in actual development. It is most important for annotations to be obtained through reflection at runtime. The content related to annotations and reflection will be learned in future articles. This concludes the understanding of annotations.

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.