天天看點

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());
      	}  
   }  
}
           

謝謝閱讀!