Java Annotation
Java 1.5中開始引入的Annotation,類似於注釋的一種技術,參考了一些網上的譯法,姑且譯成注解吧。
我們在開發中,用得最多的Annotation莫過於@Override了。大家天天用,可能很多同學卻沒有關注過其背後的細節,我們看一下它的定義:
Java代碼
- @Target(ElementType.METHOD)
- @Retention(RetentionPolicy.SOURCE)
- public @interface Override {
- }
在Android源碼中,這個注解定義在libcore/luni/src/main/java/java/lang/Override.java中。
這是一個標稱注解,只在源代碼級有效,主要被編譯器用來判斷是否真的繼承了父類中的方法。
常用的Annotation還有著名的@Deprecated,過時的建議不用的方法。
它的定義如下:
Java代碼
- @Documented
- @Retention(RetentionPolicy.RUNTIME)
- public @interface Deprecated {
- }
另外還有通知編譯器不要做警告的@SuppressWarnings
Java代碼
- @Target( { ElementType.TYPE, ElementType.FIELD, ElementType.METHOD,
- ElementType.PARAMETER, ElementType.CONSTRUCTOR,
- ElementType.LOCAL_VARIABLE })
- @Retention(RetentionPolicy.SOURCE)
- public @interface SuppressWarnings {
-
- /**
- * The list of warnings a compiler should not issue.
- */
- public String[] value();
- }
元注解
除了上面幾個常用的注解定義於最基本的java.lang包中,用於實現這幾個Annotation的Annotation都實現在java.lang.annotation包中。它們是被用於實現其它注解所用的。
元素類型 - ElementType
就是這個注解可以用於什麼語法單元,比如@Override只能用於方法,方法的類型就是ElementType.METHOD.
這是一個枚舉,包括下面的類型:
TYPE: 類,接口,枚舉
FIELD: 域變量
METHOD:方法
PARAMETER:函數參數
CONSTRUCTOR:構造函數
LOCAL_VARIABLE:局部變量
ANNOTATION_TYPE:注解類型
PACKAGE:包
@Target
定義了元素類型,就可以通過這些類型來指定注解適用的類型了。
Java代碼
- @Documented
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.ANNOTATION_TYPE)
- public @interface Target {
- ElementType[] value();
- }
@Target注解就是一個ElementType的數組,就像上面我們看到的用法:
Java代碼
- @Target( { ElementType.TYPE, ElementType.FIELD, ElementType.METHOD,
- ElementType.PARAMETER, ElementType.CONSTRUCTOR,
- ElementType.LOCAL_VARIABLE })
@Documented
Java代碼
- @Documented
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.ANNOTATION_TYPE)
- public @interface Documented {
- }
用於描述一個注解是可以生成JavaDoc的,也暗示了這個注解是一個公開的API。
@Retention
這是4個元注解中最重要的一個,用於定義這個注解的生命周期。
取值是另一個枚舉:
• RetentionPolicy.SOURCE:注解只存在於源代碼中,編譯器生成class代碼時就忽略了
• RetentionPolicy.CLASS:會編譯進class文件,但是VM執行時調不到
• RetentionPolicy.RUNTIME:在運行時也可以訪問到
RetentionPolicy的定義如下:
Java代碼
- public enum RetentionPolicy {
- /**
- * Annotation is only available in the source code.
- */
- SOURCE,
- /**
- * Annotation is available in the source code and in the class file, but not
- * at runtime. This is the default policy.
- */
- CLASS,
- /**
- * Annotation is available in the source code, the class file and is
- * available at runtime.
- */
- RUNTIME
- }
有了這些基礎,再看@Retention的實現,就可以完全看得懂了:
Java代碼
- @Documented
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.ANNOTATION_TYPE)
- public @interface Retention {
- RetentionPolicy value();
- }
@Retention本身是個運行時可用的注解,公開的API,只對注解本身有效。
它只定義了一個RetentionPolicy枚舉的值。
通過反射處理注解
所有的@Target可用的對象都支持用getAnnotations()方法去讀取注解。
例如,讀取一個類的注解:
Java代碼
- Class clazz = ThreadSafeCounter.class;
- Annotation[] as = clazz.getAnnotations();
我們通過一個例子來說明:
首先先定義一個注解,這個注解用於說明這類或者方法是線程安全的,有一個value用於保存鎖對象的名字。
Java代碼
- import java.lang.annotation.*;
-
- @Documented
- @Retention(RetentionPolicy.RUNTIME)
- @Target({ElementType.TYPE,ElementType.METHOD})
- public @interface ThreadSafe {
- String value();
- }
下面定義一個使用該注解的類, 類和其中的一個方法都使用這個注解。其實有點廢話,類都線程安全了,方法還能不安全麼,呵呵
Java代碼
- import java.lang.annotation.Annotation;
- import java.lang.reflect.Method;
-
- @ThreadSafe("ThreadSafeCounter")
- public class ThreadSafeCounter {
- private int mCounter;
-
- public ThreadSafeCounter(int counter) {
- mCounter = counter;
- }
-
- @ThreadSafe("this")
- public int incAndGet() {
- synchronized (this) {
- return mCounter++;
- }
- }
下面定義一個main方法去通過反射讀注解,先讀類的注解:
Java代碼
- public static void main(String[] args){
- Class clazz = ThreadSafeCounter.class;
- Annotation[] as = clazz.getAnnotations();
-
- for(Annotation a:as){
- ThreadSafe t= (ThreadSafe)a;
- System.out.println("Annotation type="+clazz.getName());
- System.out.println("lock name is:"+t.value());
- }
然後再讀取
Java代碼
- Method[] methods = clazz.getMethods();
- for(Method method: methods){
- boolean hasAnno = method.isAnnotationPresent(ThreadSafe.class);
- if(hasAnno){
- ThreadSafe anno = method.getAnnotation(ThreadSafe.class);
- System.out.println("method name="+method.getName()+",lock object="+anno.value());
- }
- }
- }
- }
本章內容參考文獻:《Java程序設計完全手冊》,王作啟,伍正雲著,北京:清華大學出版社,2014
Android中的Annotation
Android標准的Annotation
@Nullable
定義:
Java代碼
- @Retention(SOURCE)
- @Target({METHOD, PARAMETER, FIELD})
- public @interface Nullable {
- }
源碼級的,可以用於方法、參數和域,表示一個方法或域的值可以合法地為空,或者是函數的返回值可以合法為空。代碼中已經針對為空的情況做了相應的處理。
這是個標稱注解。
@NonNull
Java代碼
- @Retention(SOURCE)
- @Target({METHOD, PARAMETER, FIELD})
- public @interface NonNull {
- }
與@Nullable相反,@NonNull要求一定不能為空。
@UiThread
定義:
Java代碼
- @Retention(SOURCE)
- @Target({METHOD,CONSTRUCTOR,TYPE})
- public @interface UiThread {
- }
表示標有該注解的方法或構造函數應該只在UI線程調用。
如果注解元素是一個類,說明該類的所有方法都應該在UI線程中調用。
@MainThread
Java代碼
- @Retention(SOURCE)
- @Target({METHOD,CONSTRUCTOR,TYPE})
- public @interface MainThread {
- }
這個是要求運行在主線程的
@WorkerThread
Java代碼
- @Retention(SOURCE)
- @Target({METHOD,CONSTRUCTOR,TYPE})
- public @interface WorkerThread {
- }
要求運行在工作線程
@IntRef
用於定義整數值。
Java代碼
- @Retention(CLASS)
- @Target({ANNOTATION_TYPE})
- public @interface IntDef {
- /** Defines the allowed constants for this element */
- long[] value() default {};
-
- /** Defines whether the constants can be used as a flag, or just as an enum (the default) */
- boolean flag() default false;
- }
我們看一個使用@IntRef例子:
Java代碼
- @IntDef({HORIZONTAL, VERTICAL})
- @Retention(RetentionPolicy.SOURCE)
- public @interface OrientationMode {}
-
- public static final int HORIZONTAL = 0;
- public static final int VERTICAL = 1;
在上面定義的@OrientationMode注釋中,可以支持的值是HORIZONTAL, VERTICAL.
然後我們再看一個使用flag的例子:
Java代碼
- @IntDef(flag = true,
- value = {
- SHOW_DIVIDER_NONE,
- SHOW_DIVIDER_BEGINNING,
- SHOW_DIVIDER_MIDDLE,
- SHOW_DIVIDER_END
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface DividerMode {}
View相關的
@RemoteView
支持RemoteView機制,這是一個運行時的注釋.
定義路徑:/frameworks/base/core/java/android/widget/RemoteViews.java
Java代碼
- /**
- * This annotation indicates that a subclass of View is alllowed to be used
- * with the {@link RemoteViews} mechanism.
- */
- @Target({ ElementType.TYPE })
- @Retention(RetentionPolicy.RUNTIME)
- public @interface RemoteView {
- }