【註解】自定義註解及元註解

1 元註解

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

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

1.1 @Target

@Target說明了Annotation所修飾的對象範圍:Annotation可用於packages、types(類、接口、枚舉、Annotation類型)、類型成員(方法、構造方法、成員變量、枚舉值)、方法參數和本地變量(如循環變量,catch參數)。在Annotation類型的聲明中使用了target可以更加明晰其修飾的目的

描述
CONSTRUCTOR 用於描述構造器
FIELD 用於描述域(即類成員變量)
LOCAL_VARIABLE 用於描述局部變量
METHOD 用於描述方法
PACKAGE 用於描述包
PARAMETER 用於描述參數
TYPE 用於描述類、接口(包括註解類型)或枚舉聲明

1.2 @Retention

@Retention定義了該Annotation被保留的時間長短。表示需要在什麼級別保存該註解信息,用於描述註解的生命週期(即被描述的註解在什麼範圍內有效)

描述
SOURCE 在源文件中有效(即源文件保留)
CLASS 在class文件中有效(即class保留)
RUNTIME 在運行時有效(即運行時保留)

1.3 @Documented

@Documented用於描述其它類型的annotation應該作爲被標註的程序成員的公共API,因此可以被例如javadoc此類的工具文檔化。Documented是一個標記註解,沒有成員。

1.4 @Inherited

@Inherited元註解是一個標記註解,@Inherited闡述了某個被標註的類型是被繼承的。如果一個使用了@Inherited修飾的annotation類型被用於一個class,則這個annotation將被用於該class的子類。

注意:@Inherited 註解類型是被標註過的class的子類所繼承。類並不從它所實現的接口繼承annotation,方法並不從它所重載的方法繼承annotation。

當@Inherited annotation類型被標註的annotation的Retention是RetentionPolicy.RUNTIME,則反射API增強了這種繼承性。如果我們使用java.lang.reflect去查詢一個@Inherited annotation類型的annotation時,反射代碼檢查將展開工作:檢查class和其父類,直到發現指定的annotation類型被發現,或者到達到繼承結構的頂層.

1.5 常見的標準Annotation

從java5版本開始,自帶了三種標準annontation類型

  • Override:java.lang.Override 是一個marker annotation類型,它被用作標註方法。它說明了被標註的方法重載了父類的方法,起到了斷言的作用。如果我們使用了這種annotation在一個沒有覆蓋父類方法的方法時,java編譯器將以一個編譯錯誤來警示。這個annotaton常常在我們試圖覆蓋父類方法而卻又寫錯了方法名時加一個保障性的校驗過程。
  • Deprecated:Deprecated也是一種marker annotation。當一個類型或者類型成員使用@Deprecated修飾的話,編譯器將不鼓勵使用這個被標註的程序元素。所以使用這種修飾具有一定的 “延續性”:如果我們在代碼中通過繼承或者覆蓋的方式使用了這個過時的類型或者成員,雖然繼承或者覆蓋後的類型或者成員並不是被聲明爲 @Deprecated,但編譯器仍然要報警。@Deprecated這個annotation類型和javadoc中的 @deprecated這個tag是有區別的:前者是java編譯器識別的,而後者是被javadoc工具所識別用來生成文檔(包含程序成員爲什麼已經過時、它應當如何被禁止或者替代的描述)。
  • SuppressWarnings:此註解能告訴Java編譯器關閉對類、方法及成員變量的警告。有時編譯時會提出一些警告,對於這些警告有的隱藏着Bug,有的是無法避免的,對於某些不想看到的警告信息,可以通過這個註解來屏蔽。SuppressWarning不是一個marker annotation。它有一個類型爲String[]的成員,這個成員的值爲被禁止的警告名。對於javac編譯器來講,被-Xlint選項有效的警告名也同樣對@SuppressWarings有效,同時編譯器忽略掉無法識別的警告名。

1.6 annotation語法

annotation語法允許在annotation名後跟括號,括號中是使用逗號分割的name=value對用於爲annotation的成員賦值:

@SuppressWarnings(value={"unchecked","fallthrough"})  
public void lintTrap() { /* sloppy method body omitted */ }  

注意:我們可以在下面的情況中縮寫annotation:當annotation只有單一成員,併成員命名爲"value="。這時可以省去"value="。比如將上面的SuppressWarnings annotation進行縮寫:

@SuppressWarnings({"unchecked","fallthrough"})  

在上述例子中SuppressWarnings annotation類型只定義了一個單一的成員,所以只有一個簡單的value={…}作爲name=value對。又由於成員值是一個數組,故使用大括號來聲明數組值。如果SuppressWarnings所聲明的被禁止警告個數爲一個時,可以省去大括號:

@SuppressWarnings("unchecked") 

2 自定義註解

2.1 自定義註解概述

使用@interface自定義註解時,自動繼承了java.lang.annotation.Annotation接口,由編譯程序自動完成其他細節。在定義註解時,不能繼承其他的註解或接口。@interface用來聲明一個註解,其中的每一個方法實際上是聲明瞭一個配置參數。方法的名稱就是參數的名稱,返回值類型就是參數的類型(返回值類型只能是基本類型、Class、String、enum)。可以通過default來聲明參數的默認值。

  • 定義註解格式
public @interface 註解名 {定義體}
  • 註解參數的可支持數據類型:
  1. 所有基本數據類型(int,float,boolean,byte,double,char,long,short)
  2. String類型
  3. Class類型
  4. enum類型
  5. Annotation類型
  6. 以上所有類型的數組
  • Annotation類型裏面的參數設定
  1. 只能用public或默認(default)這兩個訪問權修飾.例如,String value();這裏把方法設爲defaul默認類型;
  2. 參數成員只能用基本類型byte,short,char,int,long,float,double,boolean八種基本數據類型和 String,Enum,Class,annotations等數據類型,以及這一些類型的數組.例如,String value();這裏的參數成員就爲String;
  3. 如果只有一個參數成員,最好把參數名稱設爲"value",後加小括號.例:下面的例子FruitName註解就只有一個參數成員。
package annotation;  
  
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;  
  
/** 
 * 水果名稱註解 
 * @author wangsheng 
 * 
 */  
@Target(ElementType.FIELD)  
@Retention(RetentionPolicy.RUNTIME)  
@Documented  
public @interface FruitName {  
    String value() default "";  
}
package annotation;  
  
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;  
  
/** 
 * 水果顏色註解 
 * @author peida 
 * 
 */  
@Target(ElementType.FIELD)  
@Retention(RetentionPolicy.RUNTIME)  
@Documented  
public @interface FruitColor {  
    /** 
     * 顏色枚舉 
     * @author wangsheng 
     * 
     */  
    public enum Color{ BULE,RED,GREEN};  
      
    /** 
     * 顏色屬性 
     * @return 
     */  
    Color fruitColor() default Color.GREEN;  
  
}
package annotation;  
import annotation.FruitColor.Color;  
public class Apple {   
    @FruitName("Apple")  
    private String appleName;  
    @FruitColor(fruitColor=Color.RED)  
    private String appleColor;  

    public void setAppleColor(String appleColor) {  
        this.appleColor = appleColor;  
    }  
    public String getAppleColor() {  
        return appleColor;  
    }  
      
      
    public void setAppleName(String appleName) {  
        this.appleName = appleName;  
    }  
    public String getAppleName() {  
        return appleName;  
    }  
      
    public void displayName(){  
        System.out.println("水果的名字是:蘋果");  
    }  
}
  • 註解元素的默認值
    註解元素必須有確定的值,要麼在定義註解的默認值中指定,要麼在使用註解時指定,非基本類型的註解元素的值不可爲null。因此, 使用空字符串或0作爲默認值是一種常用的做法。這個約束使得處理器很難表現一個元素的存在或缺失的狀態,因爲每個註解的聲明中,所有元素都存在,並且都具有相應的值,爲了繞開這個約束,我們只能定義一些特殊的值,例如空字符串或者負數,一次表示某個元素不存在,在定義註解時,這已經成爲一個習慣用法
package annotation;  
  
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;  
  
/** 
 * 水果供應者註解 
 * @author wangsheng 
 * 
 */  
@Target(ElementType.FIELD)  
@Retention(RetentionPolicy.RUNTIME)  
@Documented  
public @interface FruitProvider {  
    /** 
     * 供應商編號 
     * @return 
     */  
    public int id() default -1;  
      
    /** 
     * 供應商名稱 
     * @return 
     */  
    public String name() default "";  
      
    /** 
     * 供應商地址 
     * @return 
     */  
    public String address() default "";  
}

定義了註解,並在需要的時候給相關類,類屬性加上註解信息,如果沒有響應的註解信息處理流程,註解可以說是沒有實用價值。如何讓註解真真的發揮作用,主要就在於註解處理方法,下一步我們將學習註解信息的獲取和處理

Author.java

/** 
 *  
 */  
package com.wsheng.aggregator.annotation;  
  
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;  
  
/** 
 * 定義作者信息,name和group 
 *  
 * @author Josh Wang(Sheng) 
 *  
 * @email  [email protected] 
 *  
 */  
@Retention(RetentionPolicy.RUNTIME)  
@Target(ElementType.METHOD)  
@Documented  
public @interface Author {  
      
    String name(); // 因爲沒有定義public,所以默認的訪問權限爲包權限,在定義時沒有指定默認值,則使用時必須指定默認值  
    String group();  
  
}

Description.java

/** 
 *  
 */  
package com.wsheng.aggregator.annotation;  
  
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;  
  
/** 
 * 定義描述信息value 
 *  
 * @author Josh Wang(Sheng) 
 *  
 * @email  [email protected] 
 *  
 */  
@Retention(RetentionPolicy.RUNTIME)  
@Target(ElementType.TYPE)  
@Documented  
public @interface Description {  
  
    String value();// 只有一個屬性時,最好定義爲value,因爲可以省略哦:)  
}

Utility.java : 使用自定義Annotation註解的類

/** 
 *  
 */  
package com.wsheng.aggregator.annotation;  
  
/** 
 * @author Josh Wang(Sheng) 
 *  
 * @email  [email protected] 
 *  
 */  
@Description(value="這是一個有用的工具類") // value可以省略  
public class Utility {  
  
    @Author(name="wangsheng", group="developer team")  
    public String work() {  
        return "work over!";  
    }  
}

AnalysisAnnotation.java

/** 
 *  
 */  
package com.wsheng.aggregator.annotation;  
  
import java.lang.reflect.Method;  
  
/** 
 *  
 *在運行時分析處理annotation類型的信息 
 *  
 * @author Josh Wang(Sheng) 
 *  
 * @email  [email protected] 
 *  
 */  
public class AnalysisAnnotation {  
      
    public static void main(String[] args) {  
        try {  
              
            // 通過運行時反射API獲得annotation信息  
            Class<?> rtClass = Class.forName("com.wsheng.aggregator.annotation.Utility");  
            Method[] methods = rtClass.getMethods();  
              
            boolean descriptionExist = rtClass.isAnnotationPresent(Description.class);  
            if (descriptionExist) {  
                Description description = (Description)rtClass.getAnnotation(Description.class);  
                System.out.println("Utility's Description --- > " + description.value());  
                  
                for (Method method : methods) {  
                    if (method.isAnnotationPresent(Author.class)) {  
                        Author author = (Author)method.getAnnotation(Author.class);  
                        System.out.println("Utility's Author ---> " + author.name() + " from " + author.group());  
                    }  
                }  
            }  
              
        } catch (ClassNotFoundException e) {  
            e.printStackTrace();  
        }  
    }  
  
}

參考:https://www.cnblogs.com/LittleSpring/p/11344614.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章