讀取標註資訊


程式碼中若使用了自訂標註,預設會將標註資訊儲存於.class檔案,可被編譯器或位元碼分析工具讀取,但執行時期無法讀取標註資訊。如果希望於執行時期讀取標註資訊,可以於自訂標註時使用java.lang.annotation.Retention搭配java.lang.annotation.RetentionPolicy列舉指定:

package java.lang.annotation;
public enum RetentionPolicy {
    SOURCE,  // 標註資訊留在原始碼(不會儲存至.class檔案)
    CLASS,   // 標註資訊會儲存至.class檔案,但執行時期無法讀取
    RUNTIME  //標註資訊會儲存至.class檔案,但執行時期可以讀取
}

RetentionPolicySOURCE的例子為@SuppressWarnings,其作用僅在告知編譯器抑制警訊,所以不必將這個資訊儲存於.class檔案,@Override也是,其作用僅在告知編譯器檢查是否真為重新定義方法。

RetentionPolicyRUNTIME的時機,在於讓標註於執行時期提供應用程式資訊,可使用java.lang.reflect.AnnotatedElement介面實作物件取得標註資訊,這個介面定義了幾個方法:

public Annotation getAnnotation(Class annotationType);
public Annotation[] getAnnotations();
public Annotation[] getDeclaredAnnotations();
public boolean isAnnotationPresent(Class annotationType);

ClassConstructorFieldMethodPackage等類別,都實作了AnnotatedElement介面,如果標註在定義時的RetentionPolicy指定為RUNTIME,就可以從ClassConstructorFieldMethodPackage等類別的實例,取得設定的標註資訊。

舉個例子來說,假設你設計了以下的標註:

package cc.openhome;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface Debug {
    String name();
    String value();
}

由於RetentionPolicyRUNTIME,可以在執行時期讀取標註資訊,例如可將@Debug用於程式中:

package cc.openhome;

public class Other {
    @Debug(name = "caterpillar", value = "2011/10/10")
    public void doOther() {
        ...略
    }
}

以下的範例可用來讀取@Debug設定的資訊,主要是透過Java的Reflection API:

package cc.openhome;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import static java.lang.System.out;

public class DebugTool {
    public static void main(String[] args) throws NoSuchMethodException {
        Class<Other> c = Other.class;
        Method method = c.getMethod("doOther");
        if(method.isAnnotationPresent(Debug.class)) {
            out.println("已設定 @Debug 標註");
            // 取得 @Debug 實例
            Debug debug = method.getAnnotation(Debug.class);
            out.printf("value: %s%n", debug.value());
            out.printf("name : %s%n", debug.name());
        } else {
            out.println("沒有設定 @Debug 標註");
        }

        Annotation[] annotations = method.getAnnotations();
        for(Annotation annotation : annotations) {
            out.println(annotation.annotationType().getName());
        }
    }
}

執行結果如下:

已設定 @Debug 標註
value: 2011/10/10
name : caterpillar
cc.openhome.Debug

一個使用標註的更實用範例,可以參考 使用小老鼠處理傾聽器