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:
- 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 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#
- @Override
- @Deprecated
- @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 methodsunused
: warning when there are unused variablesunchecked
: warning when performing unchecked conversionsfallthrough
: warning when aswitch
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 lacksserialVersionUID
definitionfinally
: warning when anyfinally
clause cannot be completed normallyall
: 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:
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:
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#
- @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 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.