1. 程式人生 > >關於註解你需要知道的

關於註解你需要知道的

什麼是註解

註解是 Java 5 的一個新特性。註解是插入你程式碼中的一種註釋或者說是一種元資料(meta data)。這些註解資訊可以在編譯期使用預編譯工具進行處理(pre-compiler tools),也可以在執行期使用 Java 反射機制進行處理。這裡存在著一個基本的規則:Annotation不能影響程式程式碼的執行,無論增加、刪除 Annotation,程式碼都始終如一的執行。註解的行為就像

系統內建標準註解:

註解的語法比較簡單,除了@符號的使用外,他基本與Java固有的語法一致,JavaSE中內建三個標準註解,定義在java.lang中:

  • @Override:用於修飾此方法覆蓋了父類的方法;
  • @Deprecated:用於修飾已經過時的方法;
  • @SuppressWarnnings:用於通知java編譯器禁止特定的編譯警告。

元註解

元註解的作用就是負責註解其他註解。Java5.0定義了4個標準的meta-annotation型別,它們被用來提供對其它 annotation型別作說明。Java5.0定義的元註解:

  • @Target,
  • @Retention,
  • @Documented,
  • @Inherited

下面一一說一下每個元註解的引數使用和作用

@Target

@Target註解的作用目標。說明了註解所修飾的物件範圍,可以更加清晰註解所修飾的目標。

作用:描述註解的使用範圍,即註解用在什麼地方

ElementType取值 作用目標
@Target(ElementType.ANNOTATION_TYPE) 註解,就是用於描述註解本身
@Target(ElementType.CONSTRUCTOR) 描述建構函式
@Target(ElementType.FIELD) 欄位(成員變數)、列舉的常量
@Target(ElementType.LOCAL_VARIABLE) 區域性變數
@Target(ElementType.METHOD) 方法
@Target(ElementType.PACKAGE)
@Target(ElementType.PARAMETER) 方法引數
@Target(ElementType.TYPE) 類、介面(包括註解型別) 或enum宣告

舉例:

@Target(ElementType.METHOD)
public @interface MethodAnnotation {
    String value() default "";
}

@Target(ElementType.FIELD)
public @interface FieldAnnotation {
    String value() default "";
}

註解MethodAnnotation可以用來描述方法,FieldAnnotation可以用來描述欄位、列舉的常量。

@Retention

@Retention定義了該註解的時間長短,即註解的生命週期。

作用:表示需要在什麼級別儲存該註釋資訊,用於描述註解的生命週期(即:被描述的註解在什麼範圍內有效)

ElementType取值 作用目標
@Retention(RetentionPolicy.SOURCE) 在原始檔中有效(即原始檔保留)
@Retention(RetentionPolicy.CLASS) 在class檔案中有效(即class保留)
@Retention(RetentionPolicy.RUNTIME) 在執行時有效(即執行時保留)
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Person {
    public String name() default "fieldName";
    public String sex() default "fieldSex";
}

Person註解的的RetentionPolicy的屬性值是RUTIME,這樣註解處理器可以通過反射,獲取到該註解的屬性值,從而去做一些執行時的邏輯處理

@Documented

@Documented將此註解包含在Javadoc中。用於描述其它型別的annotation應該被作為被標註的程式成員的公共API,因此可以被例如Javadoc此類的工具文件化。Documented是一個標記註解,沒有成員。

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Person {
    public String name() default "fieldName";
    public String sex() default "fieldSex";
}

@Inherited

允許子類繼承父類中的註解。使用很少。

自定義註解

@interface用來宣告一個註解,其中的每一個方法實際上是聲明瞭一個配置引數。方法的名稱就是引數的名稱,返回值型別就是引數的型別(返回值型別只能是基本型別、Class、String、enum)。可以通過default來宣告引數的預設值。

定義註解格式:

public @interface 註解名 {定義體}
 
舉例:

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

註解引數的可支援資料型別:

  • 1.所有基本資料型別(int,float,boolean,byte,double,char,long,short)
  • 2.String型別
  • 3.Class型別
  • 4.enum型別
  • 5.Annotation型別
  • 6.以上所有型別的陣列

Annotation型別裡面的引數該怎麼設定:

  • 第一,只能用public或預設(default)這兩個訪問權修飾.例如,String value();這裡把方法設為defaul預設型別;   
  • 第二,引數成員只能用基本型別byte,short,char,int,long,float,double,boolean八種基本資料型別和String,Enum,Class,annotations等資料型別,以及這一些型別的陣列.例如,String value();這裡的引數成員就為String;  
  • 第三,如果只有一個引數成員,最好把引數名稱設為”value”,後加小括號.

下面用一個自定義註解的例子來說明一下使用:

最近《中國有嘻哈》挺火,那我們就讓rapper吳亦凡來dis一句 “你有freestyle嗎?”

/**
 * Created by zhuqiguang on 17/7/6.
 * rapper名字
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RapperName {
    String value() default "";
}

是哪裡的rapper呢?

/**
 * Created by zhuqiguang on 17/7/6.
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface RapperAdress {
    //住址列舉
    enum Adress{SICHUAN, BEIJING, SHANDONG}
    //住址屬性
    Adress rapperAdress() default Adress.BEIJING;
}

rapper需要嘻哈裝備

/**
 * Created by zhuqiguang on 17/7/6.
 */
public @interface RapperEquipment {
    //語言選項
    enum  Language{CHINESE, ENGLISH}
    //語言屬性
    Language rapperLanguage() default Language.CHINESE;

    //說唱歌詞
    String rapperLyrics() default "";
    //穿戴選項
    enum Clothing{GLASSES,  RING}
    Clothing rapperClothing() default Clothing.GLASSES;
}

組合成一個rapper

/**
 * Created by zhuqiguang on 17/7/6.
 */
public class Rapper {
    @RapperName("吳亦凡")
    private String rapperName;
    @RapperAdress(rapperAdress = RapperAdress.Adress.BEIJING)
    private String rapperAdress;
    @RapperEquipment(rapperLanguage = RapperEquipment.Language.CHINESE, rapperLyrics = "你有free style嗎?", rapperClothing = RapperEquipment.Clothing.GLASSES)
    private String rapperEquipment;

    public String getRapperName() {
        return rapperName;
    }

    public void setRapperName(String rapperName) {
        this.rapperName = rapperName;
    }

    public String getRapperAdress() {
        return rapperAdress;
    }

    public void setRapperAdress(String rapperAdress) {
        this.rapperAdress = rapperAdress;
    }

    public String getRapperEquipment() {
        return rapperEquipment;
    }

    public void setRapperEquipment(String rapperEquipment) {
        this.rapperEquipment = rapperEquipment;
    }
}

利用反射來獲取rapper欄位

/**
 * Created by zhuqiguang on 17/7/6.
 */
public class RapperUtils {
    private static String Tag = "RapperUtils";

    public static void getRapperInfo (Class<?> clazz){
        String rapperName = "rapper名字: ";
        String rapperAdress = "rapper住址: ";
        String rapperEquipment = "rapper裝備: ";
        Field[] declaredFields = clazz.getDeclaredFields();
        for(Field field: declaredFields) {
            if (field.isAnnotationPresent(RapperName.class)) {
                RapperName name = field.getAnnotation(RapperName.class);
                Log.d(Tag, rapperName + name.value());
            }else if (field.isAnnotationPresent(RapperAdress.class)) {
                RapperAdress adress = field.getAnnotation(RapperAdress.class);
                Log.d(Tag, rapperAdress + adress.rapperAdress().toString());
            }else if (field.isAnnotationPresent(RapperEquipment.class)) {
                RapperEquipment equipment = field.getAnnotation(RapperEquipment.class);
                String equ = "語言: " + equipment.rapperLanguage().toString() + " ,歌詞: " + equipment.rapperLyrics() + " ,穿戴: " + equipment.rapperClothing().toString();
                Log.d(Tag, rapperEquipment + equ);
            }
        }
    }
}

main函式中呼叫

RapperUtils.getRapperInfo(Rapper.class);

輸出結果:

rapper住址: BEIJING                                          
rapper裝備: 語言: CHINESE ,歌詞: 你有free style嗎? ,穿戴: GLASSES     
rapper名字: 吳亦凡                                              

總結

註解在對於框架的構建以及簡約程式碼有很大的作用,像Android中的通訊元件Router就是利用註解來設定通訊的路徑,還有著名的Butternife,其原理是ButterKnifeProcessor在編譯時會掃描你的Java程式碼中所有使用@BindView,@OnClick等註解,如果發現存在註解,那麼通過一系列的解析工作,生成一個類似AnnotationActivity## ViewBinder(className##ViewBinder)的Java類,這個類實現了ViewBinder介面。這個生成類中實現了各註解對應的程式碼像@BindView最終會執行findViewById(),@OnClick最終會執行setOnClickListener()

本文demo中的原始碼地址:

參考