天天看点

Java使用@interface定义注解元注解@Target

java用@interface定义注解。在程序中加上注解,JAVAC编译器和VM可以利用它来做一些相应的处理。

元注解

元注解是注解的注解。有如下这一些:

@Retention

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

表明带注解类型的注解将会保留多久。如果一个注解类型的声明上没有Retention注解,那么它默认的保留策略是RetentionPolicy.CLASS。

Retention注解有一个属性value,它是用来指定保留策略的。这个属性的类型是RetentionPolicy,它的值有:

public enum RetentionPolicy {
    /**
     * Annotations are to be discarded by the compiler.
     */
    SOURCE,

    /**
     * Annotations are to be recorded in the class file by the compiler
     * but need not be retained by the VM at run time.  This is the default
     * behavior.
     */
    CLASS,

    /**
     * Annotations are to be recorded in the class file by the compiler and
     * retained by the VM at run time, so they may be read reflectively.
     *
     * @see java.lang.reflect.AnnotatedElement
     */
    RUNTIME
}

           

(1)SOURCE:表示注解的信息会被编译器丢弃,不会保留在class文件中,注解的信息只会留在源文件中,所以这里注解信息一般都用在编译阶段。

(2) CLASS :表示注解的信息会被编译器记录在class文件中;但在运行时,不会被虚拟机保留着,所以它们不可以用反射的方式读取到;如果不指定Retention的值,默认就是CLASS;

(3)RUNTIME:表示注解的信息会被编译器保留在class文件里;在运行时,还会被虚拟机保留着,所以它们可以用反射的方式读取到,所以这里注解信息一般都用在程序运行阶段;

例子:

@Retention(RetentionPolicy.SOURCE)
public @interface MyAnnotation1 {
}

@Retention(RetentionPolicy.CLASS)
public @interface MyAnnotation2 {
}

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation3 {
}
           

@Target

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    /**
     * Returns an array of the kinds of elements an annotation type
     * can be applied to.
     * @return an array of the kinds of elements an annotation type
     * can be applied to
     */
    ElementType[] value();
}
           

表明注解类型适用的上下文。如果@Target元注解没有在注解类型上,那么这个注解可以作为一个修饰符用在任何声明上,除了类型参数声明。如有注解类型上有@Target元注解,那么编译器将根据ElementType枚举常量限制它的使用范围。@Target有一个属性ElementType[]类型的属性 value(),就是通过它来指定的。ElementType枚举常量有以下这些:

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

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

    /** Method declaration */
    METHOD,

    /** Formal parameter declaration */
    PARAMETER,

    /** Constructor declaration */
    CONSTRUCTOR,

    /** Local variable declaration */
    LOCAL_VARIABLE,

    /** Annotation type declaration */
    ANNOTATION_TYPE,

    /** Package declaration */
    PACKAGE,

    /**
     * Type parameter declaration
     *
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * Use of a type
     *
     * @since 1.8
     */
    TYPE_USE
}

           
  • TYPE: 类、接口(包括注解类型)、枚举声明
  • FIELD:字段声明(包括枚举常量)
  • METHOD:方法声明
  • PARAMETER:形式参数声明
  • CONSTRUCTOR:构造函数声明
  • LOCAL_VARIABLE:局部变量声明
  • ANNOTATION_TYPE:注释类型声明
  • PACKAGE:包声明
  • TYPE_PARAMETER:类型参数声明
  • TYPE_USE:类型的使用

这个@Target元注解表示声明的类型是仅用作复杂注解类型中的成员类型声明。它不能直接用于注释任何内容,如:

@Target({})
 public @interface MemberType {
      ...
  }
           

综合例子

@Documented
@Retention(CLASS)
@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE})
public @interface NonNull {
}
           

我们常自定义用@Retention(RetentionPolicy.RUNTIME) 注释的注解,因为这样的注解会保留在VM中,这样我们才有办法通过反射技术来读取到注解,再做相应处理,事不宜迟,我们来自定义一个:

@Retention(RetentionPolicy.RUNTIME)  
public @interface MyAnnotation{  
 	String city() default "Test";  
 	String name();  
  	int[] array() default { 2, 4, 5, 6 };  
  	EnumTest.TrafficLamp lamp() ;  
  	TestAnnotation lannotation() default @TestAnnotation(value = "ddd");  
  	Class style() default String.class;  
}
           

上面程序中,定义一个注解@MyAnnotation,定义了6个属性,他们的名字为:

city,name,array,lamp,lannotation,style.

属性city类型为String,默认值为gege  
属性name类型为String,没有默认值  
属性array类型为数组,默认值为2,4,5,6  
属性lamp类型为一个枚举,没有默认值  
属性lannotation类型为注解,默认值为@TestAnnotation,注解里的属性是注解  
属性style类型为Class,默认值为String类型的Class类型  
           

使用注解MyAnnotation:

@MyAnnotation( city= "beijing", name="zhijincheng",array={},lamp=TrafficLamp.RED,style=int.class)  
public class TestExample  
{  
 @MyAnnotation(lannotation=@TestAnnotation(value="baby"), name = "zhijincheng",array={1,2,3},lamp=TrafficLamp.YELLOW)  
 @Deprecated  
 @SuppressWarnings("")  
 public void read()  
 {  
  System.out.println("output something!");  
 }  
}
           

通过反射读取注解的信息:

public class MyAnnotationReflection{  
	public static void main(String[] args) throws Exception{
		TestExample testExample = new TestExample();  
       // 如果TestExample类名上有注解@MyAnnotation修饰,则为true
       if(TestExample.class.isAnnotationPresent(MyAnnotation.class)){
       		System.out.println("There is MyAnnotation");
       	}
       	
       	Class<TestExample> c = TestExample.class;  
    	Method method = c.getMethod("read", new Class[] {});
       if (method.isAnnotationPresent(MyAnnotation.class)){
       		// 调用read方法
       		method.invoke(testExample, null);
       		// 获取方法上注解@MyAnnotation的信息
       		MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);
       		String city = myAnnotation.city();
       		String name = myAnnotation.name();
       		// 打印属性city和name的值  
       		System.out.println(city + ", " + name);
       		// 打印属性array数组的长度  
       		System.out.println(myAnnotation.array().length);
       		// 打印属性lannotation的值  
       		System.out.println(myAnnotation.lannotation().value());
   			System.out.println(myAnnotation.style());  
   		}
    	// 得到read方法上的所有注解  
     	Annotation[] annotations = method.getAnnotations();  
      	for (Annotation annotation : annotations){
      		System.out.println(annotation.annotationType().getName());
      	}  
   }  
}
           

谢谢阅读!