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:
- Annotation functionality
- Define annotations
- Built-in annotations
- Meta-annotations
- 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
inButterKnife
. - 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#
- @Override
- @Deprecated
- @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 methodsunused
: Warning when there are unused variablesunchecked
: Warning when performing unchecked conversionsfallthrough
: Warning when theswitch
program block directly goes to the next case withoutbreak
path
: Warning when there are non-existent paths in class paths, source file paths, etc.serial
: Warning when a serializable class lacks aserialVersionUID
definitionfinally
: Warning when anyfinally
clause cannot be completed normallyall
: 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:
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:
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#
- @Target
- @Retention
- @Documented
- @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.