banner
jzman

jzman

Coding、思考、自觉。
github

Java系列之註解

Java 注解(Annotation)又稱之為 Java 標註、元數據,是 Java 1.5 之後加入的一種特殊語法,通過注解可以標註 Java 中的類、方法、屬性、參數、包等,可以通過反射原理對這些元數據進行訪問,注解的使用不會影響程序的正常運行,只會對編譯器警告等輔助工具產生影響,主要內容如下:

  1. 注解功能
  2. 定義注解
  3. 內置注解
  4. 元注解
  5. 類型注解

注解功能#

  • 編譯器可以使用注解來檢測錯誤和取消警告;
  • 使用注解可以生成特定代碼,如 ButtferKnife 使用注解簡化 findViewById等;
  • 某些注解可以在運行時進行檢查和操作。

定義注解#

注解的定義使用 @interface 作為關鍵字,實際上表示自動繼承了 java.lang.annotation.Annotation 接口,定義格式參考如下:

@元注解
public @interface AnnotationName{
    //配置參數(參數類型 參數名稱())
    String name() default "hello";
}

配置參數裡面的類型包括基本類型、Stringclass、枚舉以及相關類型的數組,可以使用 default設置配置參數的默認值,定義一個注解具體如下:

@Target(value = {ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface TestDefineAnnotation {
	String[] name() default "test";
}

內置注解#

  1. @Override
  2. @Deprecated
  3. @SuppressWarnings

下面是上面三個內置注解的聲明:

//表示當前的方法將覆蓋超類中的方法,編譯時進行格式檢查
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
//表示一個類或者是方法不再建議使用,將其標記為過時,但還是可以使用
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    String[] value();
}
//表示關閉不當的編譯器警告信息
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}

根據對上面三個注解的聲明來看,@SuppressWarnings 中定義了一個數組,這個數組表示在該注解上具體的目標是那些,如可在 SuppressWarnings 上使用的值,常用的具體如下:

  • deprecation:使用了過時的類或方法時的警告
  • unused:有未使用的變量時的警告
  • unchecked:執行了未檢查的轉換時的警告
  • fallthrough:當 switch 程序塊直接通往下一種情況而沒有 break 時的警告
  • path:在類路徑、源文件路徑等中有不存在的路徑時的警告
  • serial:當在可序列化的類上缺少serialVersionUID 定義時的警告
  • finally :任何 finally 子句不能正常完成時的警告
  • all:關於以上所有情況的警告

下面看一個案例,具體如下:

public void test() {
	long date = Date.parse("2018-04-22");
}

上面的代碼如果使用 eclipse 等其他 IDE 時會出現兩個警告,一是使用了過時的 API,二是變量 date賦值後沒有被使用過,警告截圖如下:

image

當然, IDE 會提示是否添加 SuppressWarnings 來取消這些警告,前文中可以看到注解 @SuppressWarnings 的聲明中需要配置參數,這個參數是一個數組,數組名稱是 value,可以省略這個名稱, 具體如下:

//不省略
public void test2() {
	@SuppressWarnings(value= {"deprecation", "unused"})
	long date = Date.parse("2018-04-22");
}
//省略
public void test2() {
	@SuppressWarnings({"deprecation", "unused"})
	long date = Date.parse("2018-04-22");
}

來張截圖說明一下使用 @SuppressWarnings 的效果,具體如下:

image

如果只想取消一種警告可以這樣寫,具體如下:

//第一種
public void test2() {
	@SuppressWarnings(value = {"deprecation"})
	long date = Date.parse("2018-04-22");
	System.out.println(date);
}
//第二種
public void test2() {
	@SuppressWarnings({"deprecation"})
	long date = Date.parse("2018-04-22");
	System.out.println(date);
}

注意:如果在定義注解的配置參數名稱為 value,那麼可以在配置注解時可以省略 value,反之,使用其他名稱,則必須採用第一種方式,要指定配置參數名稱。

當然其他注解和 @SuppressWarnings 也比較類似, @Override@Deprecated 由它們的聲明可知直接使用即可,不需要指定具體目標,在其聲明注解時用到了 @Documented@Retention@Target 等,這些用來注解其他注解的特殊注解稱之為元注解,具體請看下文。

元注解#

  1. @Target
  2. @Retention
  3. @Documented
  4. @Inherited

@Target#

@Target 用來描述注解的使用範圍,它的聲明如下:

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

@Target 聲明可知使用 @Target 注解必須指定具體的 Java 成員,也就是該注解要使用到哪個位置,具體由枚舉 ElementType中定義,具體如下:

public enum ElementType {
    TYPE,           //類、接口、注解、枚舉
    FIELD,          //屬性(包括枚舉常量) 
    METHOD,         //方法
    PARAMETER,      //參數 
    CONSTRUCTOR,    //構造方法
    LOCAL_VARIABLE, //局部變量
    ANNOTATION_TYPE,//注解
    PACKAGE,        //包

    /**
     * 類型注解
     * @since 1.8
     */
    TYPE_PARAMETER,
    TYPE_USE
}

@Retention#

@Retention 表示在什麼級別保存該注解的信息,它的聲明如下:

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

@Retention 的聲明可知,使用 @Retention 時,必須指定保存 celue(RetentionPolicy),具體值如下:

public enum RetentionPolicy {
    SOURCE,  //在編譯時會被丟棄,僅僅在源碼中存在
    CLASS,   //默認策略,運行時就會被丟棄,僅僅在 class 文件中
    RUNTIME  //編譯時會將注解信息記錄到class文件,運行時任然保留,可以通過反射獲取注解信息
}

@Documented@Inherited 都沒有配置參數,是一種標記注解,@Documented 表示將該注解顯示到用戶文檔中,@Inherited 表示該注解只有使用在類上才會有效,而且該注解會被子類繼承。

類型注解#

在對元注解的說明中可知從 Java8 開始新增了類型注解,如果在注解 @Target 使用這種注解,表明該注解可以在對應的任何地方使用,如在 @Target 中指定 TYPE_PARAMETER 就可在自定義類型的聲明處使用該注解,如在 @Target 中指定 TYPE_USE 就可在任何類型前添加該類之間,主要是方便 Java 開發者使用類型注解和相關插件(Checker Framework)來檢查來在編譯期檢查運行時的異常。

下面分別定義指定 TYPE_PARAMETER 和 TYPE_USE 的注解,具體如下:

//1. TYPE_PARAMETER
@Target(value = {ElementType.TYPE_PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface TypeParameterAnnotation {
	String value();
}
//2. TYPE_USE
@Target(value = ElementType.TYPE_USE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TypeUseAnnotation {
}

然後,在下面的案例中使用這兩個注解,具體如下:

/**
 * 測試注解
 * @author jzman
 */
public class TestAnnotation {

    //...
    
	/**
	 * ElementType.TYPE_PARAMETER
	 * 使用在自定義類型聲明的時候,如注解@TypeParameterAnnotation
	 * @param <T>
	 */
	static class TypeAnnotationA<@TypeParameterAnnotation(value="hello") T>{
		/**
		 * ElementType.TYPE_USE
		 * 可以使用在任意類型前面(包含TYPE_PARAMETER)
		 */
		//創建實例
		MyType myType = new @TypeUseAnnotation MyType();
		//對象類型
		Object obj = (@TypeUseAnnotation Object) myType;
		//泛型
		ArrayList<@TypeUseAnnotation T> list = new ArrayList<>();
		//參數中的類型
		public String testA(@TypeUseAnnotation String test) {
			return "Hello"+test;
		}
		//枚舉
		public void testB(@TypeUseAnnotation Color color) {
			//...
		}
		
		enum Color{
			RED, GREEN, BLUE
		}
	}
	
	static class MyType{}	
}

其實注解的語法比較簡單,僅僅定義注解對實際開發是沒有幫助的,覺得注解只有在運行時通過反射獲取注解信息才是最重要的,注解與反射相關的內容會在以後的推文中學習,到此對注解的認識就結束了。

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。