iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 13
0
Mobile Development

Android 十全大補系列 第 13

[Android 十全大補] Annotation

annotation 是種可以加註在程式碼物件上的一種標記物件(metadata of code),是的,聽起來非常單純,相信大家每天都會使用到但卻幾乎忘了它的存在,其實很多很功能強大的 library 都有利用 annotation 來完成某部分的功能,像是我們昨天提到的 retrofit、gson 以及我們之後會介紹的 dagger 等 。

Annotation 到底有什麼厲害之處,我們又可以怎麼使用呢?我們先來看看一個簡單的 annotation 的定義。

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

Override 是 Java 提供讓我們在 class 間繼承複寫的 function 比較不會出錯的一個 annotation,以下是我們怎麼使用的例子:

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    //...
}

使用上要以 @ 開頭來表示 annotation 的引用,眼尖的大家會疑問為什麼我們用 java 來舉例呢,如果大家回頭看自己的 kotlin 版本的 onCreate 會發現長這個樣子:

override fun onCreate(savedInstanceState: Bundle?) {
    //...
}

override 在 kotlin 被內建成一個關鍵字了,而且是強制不可省略的,所以當然也沒有必要再使用 Java 版本的 @Override 了。(謎之聲:@Override 你被 override 了)

我們在看另一個常使用的 annotation - Deprecated,有意思的是你在 Java 跟 kotlin 二種不同檔案下會連結到二個不同的 class 如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.METHOD, ElementType.PACKAGE, ElementType.PARAMETER, ElementType.TYPE})
public @interface Deprecated {
}
@Target(CLASS, FUNCTION, PROPERTY, ANNOTATION_CLASS, CONSTRUCTOR, PROPERTY_SETTER, PROPERTY_GETTER, TYPEALIAS)
@MustBeDocumented
public annotation class Deprecated(
    val message: String,
    val replaceWith: ReplaceWith = ReplaceWith(""),
    val level: DeprecationLevel = DeprecationLevel.WARNING
)

對比下我們會發現 kotlin 的 annotation 定義是 annotation class,然後二邊的 annotation 的定義通常也會搭配其他的 annotation 如 @Target@Retention,回頭翻文件會發現以下的定義:

Target 跟 Retention 也有 Java 跟 kotlin 二個版本,但大致上概念是相通的。

  • Target:
    指定被標注的 annotation 可以合法的作用在哪些程式碼物件上的 annotation,有以下這些合法的值:
public enum class AnnotationTarget {
    /** Class, interface or object, annotation class is also included */
    CLASS,
    /** Annotation class only */
    ANNOTATION_CLASS,
    /** Generic type parameter (unsupported yet) */
    TYPE_PARAMETER,
    /** Property */
    PROPERTY,
    /** Field, including property's backing field */
    FIELD,
    /** Local variable */
    LOCAL_VARIABLE,
    /** Value parameter of a function or a constructor */
    VALUE_PARAMETER,
    /** Constructor only (primary or secondary) */
    CONSTRUCTOR,
    /** Function (constructors are not included) */
    FUNCTION,
    /** Property getter only */
    PROPERTY_GETTER,
    /** Property setter only */
    PROPERTY_SETTER,
    /** Type usage */
    TYPE,
    /** Any expression */
    EXPRESSION,
    /** File */
    FILE,
    /** Type alias */
    @SinceKotlin("1.1")
    TYPEALIAS
}

所以如果一個 annotation 被標注了 CLASS@Target 那就代表這個 annotation 只能被標注在 class 上面囉!

  • Retention:
    指定 annotation 的生命週期,有以下這三種值:
public enum class AnnotationRetention {
    /** Annotation isn't stored in binary output */
    SOURCE,
    /** Annotation is stored in binary output, but invisible for reflection */
    BINARY,
    /** Annotation is stored in binary output and visible for reflection (default retention) */
    RUNTIME
}

SOURCE 代表 compile 之後這個 annotation 不會保留,BINARY 代表會存在 class 裡但不能使用,RUNTIME 不只存在 class 還可以透過 reflection 拿到這些資訊。

我們可以找些 annotation 來驗證一下我們是否真的了解,回頭看一下我們上一張所提到 Retrofit 的 @GET@GET 被標注了 @Target(METHOD),所以只能加在 function 的程式碼上,而 @Retention(RUNTIME) 代表我們可以在 runtime 時拿回這個 annotation,所以 Retrofit 才能在 runtime 時依據我們的 annotation 參數建構出一個 http 連線。

@Documented
@Target(METHOD)
@Retention(RUNTIME)
public @interface GET {
  String value() default "";
}

另一個例子是之前提到的 @Override,它的 retention 只有 SOURCE,所以 compile 之後這個資訊就會消失,這也非常合理
因為我們只需要在寫 code 的時候參考 Override,順利 compile 之後有沒有這個 annotation 就不是這麼重要了,是吧。

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

以上就是今天的內容囉,明天我們繼續聊聊怎麼 使用 annotation processing

參考資料:
https://kotlinlang.org/docs/reference/annotations.html
https://docs.oracle.com/javase/tutorial/java/annotations/basics.html

Android 十全大補已經正式出書上架囉!
有興趣的讀者歡迎參考:
https://www.tenlong.com.tw/products/9789864345786


上一篇
[Android 十全大補] Retrofit
下一篇
[Android 十全大補] Annotation Processing
系列文
Android 十全大補30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言