Java Annotation基礎

基本參考自Java Annotation Tutorial doc:http://docs.oracle.com/javase/tutorial/java/annotations/index.html, 但因爲不是全文翻譯,也加了些自己的思考,所以選了原創


1. Annotation

Java Annotation是一種元數據(metadata),不是Java程序(邏輯)的一部分。Annotation有以下用途:

1.1 爲編譯器提供信息: 編譯器可以根據annotation檢查Error或者忽略Warning

1.2 編譯和發佈時信息處理: 一些工具可以利用Java code裏的annotation信息生成必要的code、XML文件等等,比如Java doctools

1.3 運行時信息處理:一些annotation可以在運行是被讀取,根據這些annotation,Java程序可以更加靈活的控制流程(比如TestNG裏使用了大量運行時可檢查的annotation)


2. Annotation基礎

2.1 Annotation格式

Annotation用@符號標識,簡單的比如:

@Override
void overrideFunction() { ... }

Annotation可以帶元素(Elements),元素放在小括號裏,有element name和element value,比如:

@Author (
name="navy", 
date="2014/10/13"
)
class TestClass { ... }
<pre name="code" class="java">@SupressWarnings(value="unchecked")
void deprecatedMethod { ... }

如果annotation只帶一個元素,可以不用知道元素名,只提供元素的值即可

@SupressWarnings("unchecked")
void deprecatedMethod { ... }

可以同時用多個annotation:

@Author(name = "Jane Doe")
@EBook
class MyClass { ... }
同一個annotation如果聲明爲repeating annotation,則可以同時在一個地方使用多次(沒有聲明的編譯器會報錯):

@Author(name = "Jane Doe")
@Author(name = "John Smith")
class MyClass { ... }

Annotation的類型可以是以下兩個package中的任一類型:java.lang or java.lang.annotation,你也可以創建自己的annotation類型


2.2 什麼地方可以使用Annotation

類、域、方法聲明以及其他程序元素,當用在聲明上的時候,通常annotation都寫在單獨的行內

在Java SE 8中,annotation還可以用於類型(Type)上,這種類型叫類型Annotation(Type Annotation),比如:

  • 創建類實例:
        new @Interned MyObject();
    
  • 類型上溯:
        myString = (@NonNull String) str;
    
  • implements 語句:
        class UnmodifiableList<T> implements
            @Readonly List<@Readonly T> { ... }
    
  • 違例聲明:
        void monitorTemperature() throws
            @Critical TemperatureException { ... }

3. 聲明(創建)一個Annotation類型

比如我們要爲每一個類添加一個annotation type,記錄這個類的作者等信息:

@interface ClassPreamble {
   String author();
   String date();
   int currentRevision() default 1;
   String lastModified() default "N/A";
   String lastModifiedBy() default "N/A";
   // Note use of array
   String[] reviewers();
}

上例可以看到,Annotation類型的定義跟接口(Interface)有點類似,只不過在interface關鍵字前加了個@符號。實際上Annotation類型是接口的一種形式。

看看Annotation的內容,裏面包含了annotation類型元素(Annotation Type Element)的聲明,有點像方法,但是沒有方法體。可以爲這些元素提供一個默認值。定義好就可以使用這個Annotation type了:

@ClassPreamble (
   author = "John Doe",
   date = "3/17/2002",
   currentRevision = 6,
   lastModified = "4/12/2004",
   lastModifiedBy = "Jane Doe",
   // Note array notation
   reviewers = {"Alice", "Bob", "Cindy"}
)
public class Generation3List extends Generation2List {

// class code goes here

}


注意,如果要讓這個annotation的內容被放到javadoc工具生成的文檔裏,就需要在annotation聲明時使用@Documented annotation:

import java.lang.annotation.*
@Documented
@interface ClassPreamble {
   String author();
   String date();
   int currentRevision() default 1;
   String lastModified() default "N/A";
   String lastModifiedBy() default "N/A";
   // Note use of array
   String[] reviewers();
}


4. 預定義的annotation類型

Java中預定義了一些annotation types,一些是編譯器相關的,一些是用在別的Annotation類型上的(又叫meta-annotation)

4.1 用於編譯器中的annotation類型(java.lang中定義):@Deprecated, @Override, @SuppressWarnings

@Deprecated 使用這個annotation的元素(class,method,field)都是不推薦再使用的,如果在code中使用編譯時會有warning。而且當一個元素被拋棄(不再使用),除了使用@Deprecated annotation外,還要添加javadoc的@deprecated annotation(注意這兩個不一樣,首字母一個大寫一個小寫):

   // Javadoc comment follows
    /**
     * @deprecated
     * explanation of why it was deprecated
     */
    @Deprecated
    static void deprecatedMethod() { }
}


@Override 提示編譯器這個元素(method)是覆蓋了一個超類中的元素,如果使用了@Override但是實際沒有覆蓋任何元素的話,編譯器會報錯

// mark method as a superclass method
   // that has been overridden
   @Override 
   int overriddenMethod() { }

@SuppressWarnings 告訴編譯器忽略指定的warning,Java有兩種類型的warning:deprecation和unchecked,下例忽略deprecation的warning

// use a deprecated method and tell 
   // compiler not to generate a warning
   @SuppressWarnings("deprecation")
    void useDeprecatedMethod() {
        // deprecation warning
        // - suppressed
        objectOne.deprecatedMethod();
    }

@SafeVarargs 在方法或構造器上使用時,表明方法內沒有對Varargs執行潛在的不安全操作,使用這個annotation類型時,Varargs相關的unchecked warning會被忽略


@FunctionalInterface 表明聲明應該是一個功能性的接口,這是Java SE 8新引入的


4.2 用在其他annotation類型上的annotation(meta-annotation): 在java.lang.annotation中定義

@Retention 指出annotation存在於哪個level

  • RetentionPolicy.SOURCE – 只存在於源碼level,編譯器看不見
  • RetentionPolicy.CLASS – 只有編譯時被編譯器使用,JVM看不見
  • RetentionPolicy.RUNTIME – 運行時環境裏也可以看見,可以被JVM使用
@Documented 被這個annotation type標識了的annotation,裏面所有的元素都會被javadoc工具用於生成文檔

@Target 限制哪些Java元素可以使用這個annotation,它指定下列之一作爲value

  • ElementType.ANNOTATION_TYPE 可用於annotation類型
  • ElementType.CONSTRUCTOR 用於構造器
  • ElementType.FIELD 用於域或者屬性
  • ElementType.LOCAL_VARIABLE 用於本地變量
  • ElementType.METHOD 用於方法
  • ElementType.PACKAGE 用於包聲明
  • ElementType.PARAMETER 用於方法的參數
  • ElementType.TYPE 用於類的任何元素
@Inherited 指出這個annotation類型可以從超類中繼承(但是默認是不能繼承的),使用了這個annotation的annotation type只能用於類聲明中。當用戶獲取一個類的annotation類型(使用了@Inherited)失敗時,會嘗試從超類中獲取。

@Repeatable Java SE 8中新加的,被標記的annotation類型可以在同一個元素上使用多次


5. 類型Annotation及便攜式類型系統

Java SE 8之前,annotation只能應用在聲明中(方法,類,域),但是現在annotation可以用於任何的使用類型的地方(Type Use),參見2.2中的實例。

類型annotation被用來強化Java程序分析中的類型檢查,它讓用戶可以自己寫一個(或使用一個別人的)類型檢查框架,與編譯器配合使用。

比如想保證一個變量絕對不會爲null,你可以寫一個plug-in來檢查,在code中給那個變量添加一個annotation指明它不能爲null:

@NonNull String str;
編譯時,將NonNull模塊包含到命令行中,編譯器在檢查到潛在的問題是會報warning,你可以修改代碼防止錯誤發生。

你可以使用多個類型檢查的模塊,每個模塊檢查不同的錯誤,這樣就可以在編譯時添加特定的檢查以避免相應錯誤,同時幫助你寫出更健壯不易出錯的代碼。

現在有很多的第三方的類型檢查模塊可以使用,比如Checker Framework created by the University of Washington

更多內容可參考:http://types.cs.washington.edu/checker-framework/


6. 重複Annotation(Repeating Annotations)

有時候我們想在同一個聲明或者類型上重複使用相同的Annotation類型,這時需要用到重複Annotation。

@Alert(role="Manager")
@Alert(role="Administrator")
public class UnauthorizedAccessException extends SecurityException { ... }
考慮到兼容性問題,重複annotations被存放在一個編譯器自動生成的container annotation中,爲此你需要以下兩個聲明:

6.1 聲明一個重複的annotation類型: 必須標識爲@Repeatable,括號中指明瞭這個annotation類型的container annotation,Schedule的container annotation就是Schedules,所以@Schedule annotations就被存放在一個@Schedules annotation中

import java.lang.annotation.Repeatable;
@Repeatable(Schedules.class)
public @interface Schedule {
  String dayOfMonth() default "first";
  String dayOfWeek() default "Mon";
  int hour() default 12;
}

6.2 聲明container annotation類型

container annotation類型必須有一個array類型的,名爲value的元素,且這個array類型的component type必須是要用到這個container annotation的repeatable annotation類型:

public @interface Schedules {
    Schedule[] value();
}

7. 獲取annotation

反射(Reflection)API裏有幾種方法可以獲取annotation相關信息,比如AnnotatedElement.getAnnotationByType(Class<T>),  AnnotatedElement.getAnnotations(Class<T>)

跟多獲取annotation的方法見:http://docs.oracle.com/javase/8/docs/api/java/lang/reflect/AnnotatedElement.html





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