結(jié)束關(guān)于注釋的討論之前(至少在本系列文章中),我想簡要地討論一下注釋的注釋。第 1 部分中所接觸的預(yù)定義注釋類型都有預(yù)定義的目的。但是在編寫自己的注釋類型時,注釋類型的目的并不總是顯而易見的。除了基本的文檔外,可能還要針對某個特定的成員類型或者一組成員類型編寫類型。這就要求您為注釋類型提供某種元數(shù)據(jù),以便編譯器保證按照預(yù)期的目的使用注釋。
當(dāng)然,首先想到的就是 Java 語言選擇的元數(shù)據(jù)形式 —— 注釋。您可以使用 4 種預(yù)定義的注釋類型(稱為 元注釋 )對您的注釋進(jìn)行注釋。我將對這 4 種類型分別進(jìn)行介紹。
最明顯的元注釋就是允許何種程序元素具有定義的注釋類型。毫不奇怪,這種元注釋被稱為
Target
。但是在了解如何使用
Target
之前,您還需要認(rèn)識另一個類,該類被稱為
ElementType
,它實際上是一個枚舉。這個枚舉定義了注釋類型可應(yīng)用的不同程序元素。清單 9 給出了完整的
ElementType
枚舉:
清單 9. ElementType 枚舉
package java.lang.annotation; public enum ElementType { TYPE, // Class, interface, or enum (but not annotation) FIELD, // Field (including enumerated values) METHOD, // Method (does not include constructors) PARAMETER, // Method parameter CONSTRUCTOR, // Constructor LOCAL_VARIABLE, // Local variable or catch clause ANNOTATION_TYPE, // Annotation Types (meta-annotations) PACKAGE // Java package } |
清單 9 中的枚舉值意義很明確,您自己可以分析其應(yīng)用的目標(biāo)(通過后面的注解)。使用
Target
元注釋時,至少要提供這些枚舉值中的一個并指出注釋的注釋可以應(yīng)用的程序元素。清單 10 說明了
Target
的用法:
package com.oreilly.tiger.ch06; import java.lang.annotation.ElementType; import java.lang.annotation.Target; /** * Annotation type to indicate a task still needs to be completed */ @Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.ANNOTATION_TYPE}) public @interface TODO { String value(); } |
現(xiàn)在,Java 編譯器將把
TODO
應(yīng)用于類型、方法、構(gòu)造函數(shù)和其他注釋類型。這樣有助于避免他人誤用您的注釋類型(或者最好的地方是,
您自己
也不會因為疲憊而誤用它)。
下一個要用到的元注釋是
Retention
。這個元注釋和 Java 編譯器處理注釋的注釋類型的方式有關(guān)。編譯器有幾種不同選擇:
- 將注釋保留在編譯后的類文件中,并在第一次加載類時讀取它。
- 將注釋保留在編譯后的類文件中,但是在運(yùn)行時忽略它。
- 按照規(guī)定使用注釋,但是并不將它保留到編譯后的類文件中。
這三種選項用
java.lang.annotation.RetentionPolicy
枚舉表示,如清單 11 所示:
package java.lang.annotation; public enum RetentionPolicy { SOURCE, // Annotation is discarded by the compiler CLASS, // Annotation is stored in the class file, but ignored by the VM RUNTIME // Annotation is stored in the class file and read by the VM } |
現(xiàn)在可以看出,
Retention
元注釋類型使用清單 11 所示的枚舉值中的一個作為惟一的參數(shù)??梢詫⒃撛⑨層糜谀淖⑨?,如清單 12 所示:
@Retention(RetentionPolicy.SOURCE) public @interface SuppressWarnings { // annotation type body } |
如清單 12 所示,這里可以使用簡寫形式,因為
Retention
只有一個成員變量。如果要將保持性設(shè)為
RetentionPolicy.CLASS
,那么什么也不需要做,因為這就是默認(rèn)行為。
下一個元注釋是
Documented
。這個元注釋也非常容易理解,部分原因是
Documented
是一個標(biāo)記注釋。您應(yīng)該還記得第 1 部分中曾經(jīng)提到,標(biāo)記注釋沒有成員變量。
Documented
表示注釋應(yīng)該出現(xiàn)在類的 Javadoc 中。在默認(rèn)情況下,注釋
不
包括在 Javadoc 中,如果花費(fèi)大量時間注釋一個類、詳細(xì)說明未完成的工作、正確完成了什么或者描述行為,那么您應(yīng)該記住這一點(diǎn)。
清單 13 說明了
Documented
元注釋的用法:
package com.oreilly.tiger.ch06;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Marker annotation to indicate that a method or class
* is still in progress.
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface InProgress { }
|
Documented
的一個實用技巧是保持性策略。注意,清單 13 中規(guī)定注釋的保持性(retention)是
RUNTIME
,這是使用
Documented
注釋類型所
必需
的。Javadoc 使用虛擬機(jī)從其類文件(而非源文件)中加載信息。確保 VM 從這些類文件中獲得生成 Javadoc 所需信息的惟一方法是將保持性規(guī)定為
RetentionPolicy.RUNTIME
。這樣,注釋就會保留在編譯后的類文件中
并且
由虛擬機(jī)加載,然后 Javadoc 可以從中抽取出來添加到類的 HTML 文檔中。
最后一個元注釋
Inherited
,可能是最復(fù)雜、使用最少、也最容易造成混淆的一個。這就是說,我們簡單地看一看就可以了。
首先考慮這樣一種情況:假設(shè)您通過定制的
InProgress
注釋標(biāo)記一個類正在開發(fā)之中,這完全沒有問題,對吧?這些信息還會出現(xiàn)在 Javadoc 中,只要您正確地應(yīng)用了
Documented
元注釋。現(xiàn)在,假設(shè)您要編寫一個新類,擴(kuò)展那個還在開發(fā)之中的類,也不難,是不是?但是要記住,那個超類還在開發(fā)之中。如果您使用子類,或者查看它的文檔,根本沒有線索表明還有什么地方?jīng)]有完成。您本來希望看到
InProgress
注釋被帶到子類中 —— 因為這是
繼承
的 —— 但情況并非如此。您必須使用
Inherited
元注釋說明所期望的行為,如清單 14 所示:
package com.oreilly.tiger.ch06;
import java.lang.annotation.Documented;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Marker annotation to indicate that a method or class
* is still in progress.
*/
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface InProgress { }
|
添加
@Inherited
后,您將看到
InProgress
出現(xiàn)在注釋類的子類中。當(dāng)然,您并不希望所有的注釋類型都具有這種行為(因此默認(rèn)值是
不
繼承的)。比如,
TODO
注釋就不會(也不應(yīng)該)被傳播。但是對于這里示范的情況,
Inherited
可能非常有用。
?
![]() ![]() |
![]()
|
?
現(xiàn)在,您也許已經(jīng)準(zhǔn)備回到 Java 世界為所有的事物編寫文檔和注釋了。這不禁令我回想起人們了解 Javadoc 之后發(fā)生的事情。我們都陷入了文檔過濫的泥潭,直到有人認(rèn)識到最好使用 Javadoc 來理清容易混淆的類或者方法。無論用 Javadoc 做了多少文章,也沒有人會去看那些易于理解的
getXXX()
和
setXXX()
方法。
注釋也可能出現(xiàn)同樣的趨勢,雖然不一定到那種程度。經(jīng)常甚至頻繁地使用標(biāo)準(zhǔn)注釋類型是一種較好的做法。所有的 Java 5 編譯器都支持它們,它們的行為也很容易理解。但是,如果要使用定制注釋和元注釋,那么就很難保證花費(fèi)很大力氣創(chuàng)建的那些類型在您的開發(fā)環(huán)境之外還有什么意義。因此要慎重。在合理的情況下使用注釋,不要荒謬使用。無論如何,注釋都是一種很好的工具,可以在開發(fā)過程中提供真正的幫助。
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
