banner
jzman

jzman

Coding、思考、自觉。
github

Java Series: Annotations

Java annotations (Annotation), also known as Java annotations and 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 principle of reflection. 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 format of the definition 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 keyword can be used to set the default value of the configuration parameters. 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 following are the declarations of the above three built-in annotations:

// Indicates that the current method will override the method in the superclass, and format checking is 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 and marks it as deprecated, but it can still be used
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    String[] value();
}
// Indicates the closure 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. The 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 the 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 a serialVersionUID definition
  • finally: Warning when any finally clause cannot be completed normally
  • all: Warnings about all the above cases

Let's take a look at an example, as follows:

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

When using IDEs such as Eclipse, two warnings will appear. One is that the deprecated API is used, and the other is that the variable date is assigned a value but 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 configuration 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 type of 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 according to their declaration annotations. There is no need to specify specific targets. @Override and @Deprecated use @Documented, @Retention, @Target, etc. when declaring annotations. These special annotations used to annotate other annotations are called meta-annotations. Please see the following content 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. 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 represents 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. The specific values are as follows:

public enum RetentionPolicy {
    SOURCE,  // Discarded at compile time, 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 at compile time 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 the annotation will be inherited by subclasses.

Type annotations#

In the explanation of meta-annotations, it can be seen that starting from Java 8, type annotations were added. If this kind of annotation is used in the @Target annotation, it means that the annotation can be used anywhere corresponding. 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 at compile time.

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 the parameter
        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. It is important to obtain annotation information through reflection at runtime. The content related to annotations and reflection will be learned in future articles. This is the end of the understanding of annotations.

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