1. 程式人生 > >Java annotation 自定義註釋@interface的用法

Java annotation 自定義註釋@interface的用法

一、什麼是註釋 

    說起註釋,得先提一提什麼是元資料(metadata)。所謂元資料就是資料的資料。也就是說,元資料是描述資料的。就象資料表中的欄位一樣,每個欄位描述了這個欄位下的資料的含義。而J2SE5.0中提供的註釋就是java原始碼的元資料,也就是說註釋是描述java原始碼的。在J2SE5.0中可以自定義註釋。使用時在@後面跟註釋的名字。 
                                                                                    
二、J2SE5.0中預定義的註釋 

    在J2SE5.0的java.lang包中預定義了三個註釋。它們是Override、Deprecated和SuppressWarnings。下面分別解釋它們的含義。 

       1.Override註釋:僅用於方法(不可用於類、包的生命或其他),指明註釋的方法將覆蓋超類中的方法(如果覆蓋父類的方法而沒有注 
釋就無法編譯該類),註釋還能確保註釋父類方法的拼寫是正確(錯誤的編寫,編譯器不認為是子類的新方法,而會報錯) 
      

[email protected]註釋:對不應再使用的方法進行註釋,與正在宣告為過時的方法放在同一行。使用被     Deprecated註釋的方法,編譯器會 
提示方法過時警告(”Warring”) 
       [email protected]註釋:單一註釋,可以通過陣列提供變數,變數值指明要阻止的特定型別警告(忽略某些警告)。陣列中的變數指明要阻止的警告@SuppressWarnings(value={”unchecked”,”fallthrough”})) 

三、自定義註釋@interface 

@interface:註釋宣告,定義註釋型別(與預設的Override等三種註釋型別類似)。請看下面例項: 

註釋類1: 
 

package a.test; 

import java.lang.annotation.Documented; 
import java.lang.annotation.ElementType; 
import java.lang.annotation.Retention; 
import java.lang.annotation.RetentionPolicy; 
import java.lang.annotation.Target; 

@Documented 
@Retention(RetentionPolicy.RUNTIME) 
@Target(ElementType.TYPE) 
public @interface FirstAnno { 
String value() default "FirstAnno"; 
} 


註釋類2: 
 

package a.test; 

import java.lang.annotation.Documented; 
import java.lang.annotation.ElementType; 
import java.lang.annotation.Retention; 
import java.lang.annotation.RetentionPolicy; 
import java.lang.annotation.Target; 

@Documented 
@Retention(RetentionPolicy.RUNTIME) 
@Target(ElementType.METHOD) 
public @interface SecondAnnotation { 
//  註釋中含有兩個引數 
    String name() default "Hrmzone"; 
    String url() default "hrmzone.cn"; 

} 



註釋類3: 
 

package a.test; 

import java.lang.annotation.Documented; 
import java.lang.annotation.ElementType; 
import java.lang.annotation.Retention; 
import java.lang.annotation.RetentionPolicy; 
import java.lang.annotation.Target; 

@Documented 
@Retention(RetentionPolicy.RUNTIME) 
@Target(ElementType.FIELD) 
public @interface Kitto { 
String value() default "kitto"; 
} 


使用類: 
 

package a.test; 
@FirstAnno("http://hrmzone.cn") 
public class Anno { 
@Kitto("測試") 
private String test = ""; 
//  不賦值註釋中的引數,使用預設引數 
    @SecondAnnotation() 
    public String getDefault() { 
            return "get default Annotation"; 
    } 
    @SecondAnnotation(name="desktophrm",url="desktophrm.com") 
    public String getDefine() { 
            return "get define Annotation"; 
    } 

} 


測試類: 
 

package a.test; 

import java.lang.reflect.Field; 
import java.lang.reflect.Method; 
import java.util.ArrayList; 
import java.util.List; 

public class AnnoTest { 
public static void main(String[] args) throws ClassNotFoundException { 
  // 要使用到反射中的相關內容 
  Class c = Class.forName("a.test.Anno"); 
  Method[] method = c.getMethods(); 
  boolean flag = c.isAnnotationPresent(FirstAnno.class); 
  if (flag) { 
   FirstAnno first = (FirstAnno) c.getAnnotation(FirstAnno.class); 
   System.out.println("First Annotation:" + first.value() + "\n"); 
  } 

  List<Method> list = new ArrayList<Method>(); 
  for (int i = 0; i < method.length; i++) { 
    list.add(method[i]); 
  } 

  for (Method m : list) { 
   SecondAnnotation anno = m.getAnnotation(SecondAnnotation.class); 
   if(anno == null) 
    continue; 
   
   System.out.println("second annotation's\nname:\t" + anno.name() 
     + "\nurl:\t" + anno.url()); 
  } 
  
  List<Field> fieldList = new ArrayList<Field>(); 
  for(Field f : c.getDeclaredFields()){//訪問所有欄位 
   Kitto k = f.getAnnotation(Kitto.class); 
   System.out.println("----kitto anno: " + k.value()); 
  } 
} 

} 



結合原始檔中註釋,想必對註釋的應用有所瞭解。下面深入瞭解。 
     深入註釋: 
     @Target:指定程式元定義的註釋所使用的地方,它使用了另一個類:ElementType,是一個列舉類定義了註釋型別可以應用到不同的程式元素以免使用者誤用。看看java.lang.annotation 下的原始碼: 
 

@Documented  
@Retention(RetentionPolicy.RUNTIME)  
@Target(ElementType.ANNOTATION_TYPE)  
public @interface Target {  
    ElementType[] value();  
} 




     ElementType是一個列舉型別,指明註釋可以使用的地方,看看ElementType類: 
public enum ElementType {  
     TYPE, // 指定適用點為 class, interface, enum  
     FIELD, // 指定適用點為 field  
     METHOD, // 指定適用點為 method  
     PARAMETER, // 指定適用點為 method 的 parameter  
     CONSTRUCTOR, // 指定適用點為 constructor  
     LOCAL_VARIABLE, // 指定使用點為 區域性變數  
     ANNOTATION_TYPE, //指定適用點為 annotation 型別  
     PACKAGE // 指定適用點為 package  

     @Retention:這個元註釋和java編譯器處理註釋的註釋型別方式相關,告訴編譯器在處理自定義註釋型別的幾種不同的選擇,需要使用RetentionPolicy列舉類。此列舉類只有一個成員變數,可以不用指明成名名稱而賦值,看Retention的原始碼: 

@Documented  
@Retention(RetentionPolicy.RUNTIME)  
@Target(ElementType.ANNOTATION_TYPE)  
public @interface Retention {  
    RetentionPolicy value();  

     類中有個RetentionPolicy類,也是一個列舉類,具體看程式碼: 

public enum RetentionPolicy {  
     SOURCE, // 編譯器處理完Annotation後不儲存在class中  
     CLASS, // 編譯器把Annotation儲存在class中,這是預設值  
     RUNTIME // 編譯器把Annotation儲存在class中,可以由虛擬機器讀取,反射需要  

     @Documented:是一個標記註釋,表示註釋應該出現在類的javadoc中,因為在預設情況下注釋時不包括在javadoc中的。 

所以如果花費了大量的時間定義一個註釋型別,並想描述註釋型別的作用,可以使用它。 

注意他與@Retention(RetentionPolicy.RUNTIME)配合使用,因為只有將註釋保留在編譯後的類檔案中由虛擬機器載入, 

然後javadoc才能將其抽取出來新增至javadoc中。 
     @Inherited:將註釋同樣繼承至使用了該註釋型別的方法中(表達有點問題,就是如果一個方法使用了的註釋用了@inherited, 

那麼其子類的該方法同樣繼承了該註釋) 
注意事項: 
     1.所有的Annotation自動繼承java.lang.annotation介面 
     2.自定義註釋的成員變數訪問型別只能是public、default;(所有的都能訪問,源作者沒用到函式:getDeclaredFields而已) 
     3.成員變數的只能使用基本型別(byte、short、int、char、long、double、float、boolean和String、Enum、Class、annotations以及該型別的資料)(沒有限制,大家可以修改測試一下,就清楚) 
     4.如果只有一個成員變數,最好將引數名稱設為value,賦值時不用制定名稱而直接賦值 
     5.在實際應用中,還可以使用註釋讀取和設定Bean中的變數。