<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆 - 1, 文章 - 14, 評(píng)論 - 3, 引用 - 0
    數(shù)據(jù)加載中……

    Java Annotation

    一、Annotation究竟是什么?

    Annotation提供了一條與程序元素關(guān)聯(lián)任何信息或者任何元數(shù)據(jù)(metadata)的途徑。從某些方面看,annotation就像修飾符一樣被使用,并應(yīng)用于包、類(lèi)型、構(gòu)造方法、方法、成員變量、參數(shù)、本地變量的聲明中。這些信息被存儲(chǔ)在annotation的“name=value”結(jié)構(gòu)對(duì)中。annotation類(lèi)型是一種接口,能夠通過(guò)java反射API的方式提供對(duì)其信息的訪問(wèn)。

    annotation能被用來(lái)為某個(gè)程序元素(類(lèi)、方法、成員變量等)關(guān)聯(lián)任何的信息。需要注意的是,這里存在著一個(gè)基本的潛規(guī)則:annotaion不能影響程序代碼的執(zhí)行,無(wú)論增加、刪除annotation,代碼都始終如一的執(zhí)行。另外,盡管一些annotation通過(guò)java的反射api方法在運(yùn)行時(shí)被訪問(wèn),而java語(yǔ)言解釋器在工作時(shí)忽略了這些annotation。正是由于java虛擬機(jī)忽略了annotation,導(dǎo)致了annotation類(lèi)型在代碼中是“不起作用”的;只有通過(guò)某種配套的工具才會(huì)對(duì)annotation類(lèi)型中的信息進(jìn)行訪問(wèn)和處理。本文中將涵蓋標(biāo)準(zhǔn)的annotation和meta-annotation類(lèi)型,陪伴這些annotation類(lèi)型的工具是java編譯器(當(dāng)然要以某種特殊的方式處理它們)。

    由于上述原因,annotation在使用時(shí)十分簡(jiǎn)便。一個(gè)本地變量可以被一個(gè)以NonNull命名的annotation類(lèi)型所標(biāo)注,來(lái)作為對(duì)這個(gè)本地變量不能被賦予null值的斷言。而我們可以編寫(xiě)與之配套的一個(gè)annotation代碼分析工具,使用它來(lái)對(duì)具有前面變量的代碼進(jìn)行解析,并且嘗試驗(yàn)證這個(gè)斷言。當(dāng)然這些代碼并不必自己編寫(xiě)。在JDK安裝后,在JDK/bin目錄中可以找到名為“apt”的工具,它提供了處理annotation的框架:它啟動(dòng)后掃描源代碼中的annotation,并調(diào)用我們定義好的annotation處理器完成我們所要完成的工作(比如驗(yàn)證前面例子中的斷言)。說(shuō)到這里,annotation的強(qiáng)大功能似乎可以替代XDoclet這類(lèi)的工具了,隨著我們的深入,大家會(huì)更加堅(jiān)信這一點(diǎn)。
    注:詳細(xì)描述請(qǐng)參看jsr250規(guī)范:
    http://www.jcp.org/aboutJava/communityprocess/pfd/jsr250/

    二、Annotation的定義:

    這段文字開(kāi)始介紹annotation相關(guān)技術(shù)。在此大家將看到j(luò)ava5.0的標(biāo)準(zhǔn)annotation類(lèi)型,這種標(biāo)準(zhǔn)類(lèi)型就是前文中所說(shuō)的“內(nèi)建”類(lèi)型,它們可以直接被javac支持。可喜的是,在java6.0beta版中的javac已經(jīng)加入了對(duì)自定義annotation的支持。

    1。Annotation的概念和語(yǔ)法:

    首先,關(guān)鍵的概念是理解annotation是與一個(gè)程序元素相關(guān)聯(lián)信息或者元數(shù)據(jù)的標(biāo)注。它從不影響java程序的執(zhí)行,但是對(duì)例如編譯器警告或者像文檔生成器等輔助工具產(chǎn)生影響。

    下面是常用的annotation列表,我們應(yīng)該注意在annotation和annotation類(lèi)型之間的不同:

    A.annotation:
    annotation使用了在java5.0所帶來(lái)的新語(yǔ)法,它的行為十分類(lèi)似public、final這樣的修飾符。每個(gè)annotation具有一個(gè)名字和成員個(gè)數(shù)>=0。每個(gè)annotation的成員具有被稱(chēng)為name=value對(duì)的名字和值(就像javabean一樣),name=value裝載了annotation的信息。

    B.annotation類(lèi)型:
    annotation類(lèi)型定義了annotation的名字、類(lèi)型、成員默認(rèn)值。一個(gè)annotation類(lèi)型可以說(shuō)是一個(gè)特殊的java接口,它的成員變量是受限制的,而聲明annotation類(lèi)型時(shí)需要使用新語(yǔ)法。當(dāng)我們通過(guò)java反射api訪問(wèn)annotation時(shí),返回值將是一個(gè)實(shí)現(xiàn)了該annotation類(lèi)型接口的對(duì)象,通過(guò)訪問(wèn)這個(gè)對(duì)象我們能方便的訪問(wèn)到其annotation成員。后面的章節(jié)將提到在java5.0的java.lang包里包含的3個(gè)標(biāo)準(zhǔn)annotation類(lèi)型。

    C.annotation成員:
    annotation的成員在annotation類(lèi)型中以無(wú)參數(shù)的方法的形式被聲明。其方法名和返回值定義了該成員的名字和類(lèi)型。在此有一個(gè)特定的默認(rèn)語(yǔ)法:允許聲明任何annotation成員的默認(rèn)值:一個(gè)annotation可以將name=value對(duì)作為沒(méi)有定義默認(rèn)值的annotation成員的值,當(dāng)然也可以使用name=value對(duì)來(lái)覆蓋其它成員默認(rèn)值。這一點(diǎn)有些近似類(lèi)的繼承特性,父類(lèi)的構(gòu)造函數(shù)可以作為子類(lèi)的默認(rèn)構(gòu)造函數(shù),但是也可以被子類(lèi)覆蓋。

    D.marker annotation類(lèi)型:
    一個(gè)沒(méi)有成員定義的annotation類(lèi)型被稱(chēng)為marker annotation。這種annotation類(lèi)型僅使用自身的存在與否來(lái)為我們提供信息。如后面要說(shuō)的Override。

    E.meta-annotation:
    meta-annotation也稱(chēng)為元annotation,它是被用來(lái)聲明annotation類(lèi)型的annotation。Java5.0提供了一些標(biāo)準(zhǔn)的元-annotation類(lèi)型。下面介紹的target、retention就是meta-annotation。

    F.target:
    annotation的target是一個(gè)被標(biāo)注的程序元素。target說(shuō)明了annotation所修飾的對(duì)象范圍:annotation可被用于packages、types(類(lèi)、接口、枚舉、annotation類(lèi)型)、類(lèi)型成員(方法、構(gòu)造方法、成員變量、枚舉值)、方法參數(shù)和本地變量(如循環(huán)變量、catch參數(shù))。在annotation類(lèi)型的聲明中使用了target可更加明晰其修飾的目標(biāo)。

    G.retention:
    annotation的retention定義了該annotation被保留的時(shí)間長(zhǎng)短:某些annotation僅出現(xiàn)在源代碼中,而被編譯器丟棄;而另一些卻被編譯在class文件中;編譯在class文件中的annotation可能會(huì)被虛擬機(jī)忽略,而另一些在class被裝載時(shí)將被讀取(請(qǐng)注意并不影響class的執(zhí)行,因?yàn)閍nnotation與class在使用上是被分離的)。使用這個(gè)meta-annotation可以對(duì)annotation的“生命周期”限制。

    H.metadata:
    由于metadata被廣泛使用于各種計(jì)算機(jī)開(kāi)發(fā)過(guò)程中,所以當(dāng)我們?cè)谶@里談?wù)摰膍etadata即元數(shù)據(jù)通常指被annotation裝載的信息或者annotation本身。

    2。使用標(biāo)準(zhǔn)Annotation:
    java5.0在java.lang包中定義了3種標(biāo)準(zhǔn)的annotation類(lèi)型:

    A.Override:
    java.lang.Override是一個(gè)marker annotation類(lèi)型,它被用作標(biāo)注方法。它說(shuō)明了被標(biāo)注的方法重載了父類(lèi)的方法,起到了斷言的作用。如果我們使用了這種annotation在一個(gè)沒(méi)有覆蓋父類(lèi)方法的方法時(shí),java編譯器將以一個(gè)編譯錯(cuò)誤來(lái)警示。
    這個(gè)annotaton常常在我們?cè)噲D覆蓋父類(lèi)方法而確又寫(xiě)錯(cuò)了方法名時(shí)發(fā)揮威力。

    使用方法極其簡(jiǎn)單:在使用此annotation時(shí)只要在被修飾的方法前面加上@Override。
    下面的代碼是一個(gè)使用@Override修飾一個(gè)企圖重載父類(lèi)的toString方法,而又存在拼寫(xiě)錯(cuò)誤的sample:

    @Override
    public String toSting() {?? // 注意方法名拼寫(xiě)錯(cuò)了
    ????return "[" + super.toString() + "]";
    }


    B.Deprecated:
    同樣Deprecated也是一個(gè)marker annotation。當(dāng)一個(gè)類(lèi)型或者類(lèi)型成員使用@Deprecated修飾的話,編譯器將不鼓勵(lì)使用這個(gè)被標(biāo)注的程序元素。而且這種修飾具有一定的“延續(xù)性”:如果我們?cè)诖a中通過(guò)繼承或者覆蓋的方式使用了這個(gè)過(guò)時(shí)的類(lèi)型或者成員,雖然繼承或者覆蓋后的類(lèi)型或者成員并不是被聲明為@Deprecated,但編譯器仍然要報(bào)警。
    值得注意,@Deprecated這個(gè)annotation類(lèi)型和javadoc中的@deprecated這個(gè)tag是有區(qū)別的:前者是java編譯器識(shí)別的,而后者是被javadoc工具所識(shí)別用來(lái)生成文檔(包含程序成員為什么已經(jīng)過(guò)時(shí)、它應(yīng)當(dāng)如何被禁止或者替代的描述)。
    在java5.0,java編譯器仍然象其從前版本那樣尋找@deprecated這個(gè)javadoc tag,并使用它們產(chǎn)生警告信息。但是這種狀況將在后續(xù)版本中改變,我們應(yīng)在現(xiàn)在就開(kāi)始使用@Deprecated來(lái)修飾過(guò)時(shí)的方法而不是@deprecated javadoc tag。

    下面是一段使用@Deprecated的代碼:
    /**
    * 這里是javadoc的@deprecated聲明.
    * @deprecated No one has players for this format any more.??Use VHS instead.
    */
    @Deprecated public class Betamax { ... }


    C.SuppressWarnings:
    @SuppressWarnings被用于有選擇的關(guān)閉編譯器對(duì)類(lèi)、方法、成員變量、變量初始化的警告。在java5.0,sun提供的javac編譯器為我們提供了-Xlint選項(xiàng)來(lái)使編譯器對(duì)合法的程序代碼提出警告,此種警告從某種程度上代表了程序錯(cuò)誤。例如當(dāng)我們使用一個(gè)generic collection類(lèi)而又沒(méi)有提供它的類(lèi)型時(shí),編譯器將提示出"unchecked warning"的警告。

    通常當(dāng)這種情況發(fā)生時(shí),我們就需要查找引起警告的代碼。如果它真的表示錯(cuò)誤,我們就需要糾正它。例如如果警告信息表明我們代碼中的switch語(yǔ)句沒(méi)有覆蓋所有可能的case,那么我們就應(yīng)增加一個(gè)默認(rèn)的case來(lái)避免這種警告。
    相仿,有時(shí)我們無(wú)法避免這種警告,例如,我們使用必須和非generic的舊代碼交互的generic collection類(lèi)時(shí),我們不能避免這個(gè)unchecked warning。此時(shí)@SuppressWarning就要派上用場(chǎng)了,在調(diào)用的方法前增加@SuppressWarnings修飾,告訴編譯器停止對(duì)此方法的警告。
    SuppressWarning不是一個(gè)marker annotation。它有一個(gè)類(lèi)型為String[]的成員,這個(gè)成員的值為被禁止的警告名。對(duì)于javac編譯器來(lái)講,被-Xlint選項(xiàng)有效的警告名也同樣對(duì)@SuppressWarings有效,同時(shí)編譯器忽略掉無(wú)法識(shí)別的警告名。

    annotation語(yǔ)法允許在annotation名后跟括號(hào),括號(hào)中是使用逗號(hào)分割的name=value對(duì)用于為annotation的成員賦值:

    @SuppressWarnings(value={"unchecked","fallthrough"})
    public void lintTrap() { /* sloppy method body omitted */ }


    在這個(gè)例子中SuppressWarnings annotation類(lèi)型只定義了一個(gè)單一的成員,所以只有一個(gè)簡(jiǎn)單的value={...}作為name=value對(duì)。又由于成員值是一個(gè)數(shù)組,故使用大括號(hào)來(lái)聲明數(shù)組值。

    注意:我們可以在下面的情況中縮寫(xiě)annotation:當(dāng)annotation只有單一成員,并成員命名為"value="。這時(shí)可以省去"value="。比如將上面的SuppressWarnings annotation進(jìn)行縮寫(xiě):

    @SuppressWarnings({"unchecked","fallthrough"})

    如果SuppressWarnings所聲明的被禁止警告?zhèn)€數(shù)為一個(gè)時(shí),可以省去大括號(hào):

    @SuppressWarnings("unchecked")


    3。Annotation語(yǔ)法:

    在上一個(gè)章節(jié)中,我們看到書(shū)寫(xiě)marker annotation和單一成員annotation的語(yǔ)法。下面本人來(lái)介紹一下完整的語(yǔ)法:

    annotation由“@+annotation類(lèi)型名稱(chēng)+(..逗號(hào)分割的name-value對(duì)...)”組成。其中成員可以按照任何的順序。如果annotation類(lèi)型定義了某個(gè)成員的默認(rèn)值,則這個(gè)成員可以被省略。成員值必須為編譯時(shí)常量、內(nèi)嵌的annotation或者數(shù)組。

    下面我們將定義一個(gè)annotation類(lèi)型名為Reviews,它有一個(gè)由@Review annotation數(shù)組構(gòu)成的成員。這個(gè)@Review annotation類(lèi)型有三個(gè)成員:"reviewer"是一個(gè)字符串,"comment" 是一個(gè)具有默認(rèn)值的可選的字符串,"grade"是一個(gè)Review.Grade枚舉類(lèi)型值。

    @Reviews({??// Single-value annotation, so "value=" is omitted here
    ????@Review(grade=Review.Grade.EXCELLENT,
    ????????????reviewer="df"),
    ????@Review(grade=Review.Grade.UNSATISFACTORY,
    ????????????reviewer="eg",
    ????????????comment="This method needs an @Override annotation")
    })

    annotation語(yǔ)法的另一個(gè)重要規(guī)則是沒(méi)有程序成員可以有多于一個(gè)的同一annotation實(shí)例。例如在一個(gè)類(lèi)中簡(jiǎn)單的放置多個(gè)@Review annotation。這也是在上面代碼中定義@Reviews annotation類(lèi)型數(shù)組的原因。

    4。Annotation成員類(lèi)型和值:

    annotation成員必須是非空的編譯時(shí)常量表達(dá)式。可用的成員類(lèi)型為:primitive類(lèi)型、, String, Class, enumerated類(lèi)型, annotation類(lèi)型, 和前面類(lèi)型的數(shù)組。

    下面我們定義了一個(gè)名為UncheckedExceptions 的annotation類(lèi)型,它的成員是一個(gè)擴(kuò)展了RuntimeException類(lèi)的類(lèi)數(shù)組。

    @UncheckedExceptions({
    ????IllegalArgumentException.class, StringIndexOutOfBoundsException.class
    })


    5。Annotation的目標(biāo):

    annotation通常被放在類(lèi)型定義和成員定義的前面。然而它也出現(xiàn)在package、方法參數(shù)、本地變量的前面。下面,我們來(lái)討論一下這些不大常用的寫(xiě)法:

    package annotation出現(xiàn)在package聲明的前面。
    下面的例子package-info.java中不包含任何的公共類(lèi)型定義,卻包含一個(gè)可選的javadoc注釋。

    /**
    * This package holds my custom annotation types.
    */
    @com.davidflanagan.annotations.Author("David Flanagan")
    package com.davidflanagan.annotations;

    當(dāng)package-info.java文件被編譯時(shí),它將產(chǎn)生名為包含annotation(特殊的接口)聲明的package-info.class的類(lèi)。這個(gè)接口沒(méi)有成員,它的名字package-info不是一個(gè)合法的java標(biāo)識(shí),所以它不能用在java源代碼中。這個(gè)接口的存在只是簡(jiǎn)單的被看作一個(gè)為package annotation準(zhǔn)備的占位符。

    用于修飾方法參數(shù)、catch參數(shù)、本地變量的annotation只是簡(jiǎn)單的出現(xiàn)在這些程序成員的修飾符位置。java類(lèi)文件格式?jīng)]有為本地變量或者catch參數(shù)存儲(chǔ)annotation作準(zhǔn)備,所以這些annotation總是保留在源代碼級(jí)別(source retention);方法參數(shù)annotation能夠保存在類(lèi)文件中,也可以在保留到運(yùn)行時(shí)。

    最后,請(qǐng)注意,枚舉類(lèi)型定義中不允許任何的修飾符修飾其枚舉值。

    6。Annotation和默認(rèn)值:
    在Annotation中,沒(méi)有默認(rèn)值的成員必須有一個(gè)成員值。而如何理解默認(rèn)值是如何被處理就是一個(gè)很重要的細(xì)節(jié):annotation類(lèi)型所定義的成員默認(rèn)值被存儲(chǔ)在class文件中,不被編譯到annotation里面。如果我們修改一個(gè)annotation類(lèi)型使其成員的默認(rèn)值發(fā)生了改變,這個(gè)改變對(duì)于所有此類(lèi)型的annotation中沒(méi)有明確提供成員值的成員產(chǎn)生影響(即修改了該成員的成員值)。即使在annotation類(lèi)型使其成員的默認(rèn)值被改變后annotation從沒(méi)被重新編譯過(guò),該類(lèi)型的annotation(改變前已經(jīng)被編譯的)也受到影響。

    三、Annotation工作原理:

    Annotation與反射
    在java5.0中Java.lang.reflect提供的反射API被擴(kuò)充了讀取運(yùn)行時(shí)annotation的能力。讓我們回顧一下前面所講的:一個(gè)annotation類(lèi)型被定義為runtime retention后,它才是在運(yùn)行時(shí)可見(jiàn),當(dāng)class文件被裝載時(shí)被保存在class文件中的annotation才會(huì)被虛擬機(jī)讀取。那么reflect是如何幫助我們?cè)L問(wèn)class中的annotation呢?

    下文將在java.lang.reflect用于annotation的新特性,其中java.lang.reflect.AnnotatedElement是重要的接口,它代表了提供查詢annotation能力的程序成員。這個(gè)接口被java.lang.Package、java.lang.Class實(shí)現(xiàn),并間接地被Method類(lèi)、Constructor類(lèi)、java.lang.reflect的Field類(lèi)實(shí)現(xiàn)。而annotation中的方法參數(shù)可以通過(guò)Method類(lèi)、Constructor類(lèi)的getParameterAnnotations()方法獲得。

    下面的代碼使用了AnnotatedElement類(lèi)的isAnnotationPresent()方法判斷某個(gè)方法是否具有@Unstable annotation,從而斷言此方法是否穩(wěn)定:

    import java.lang.reflect.*;

    Class c = WhizzBangClass.class;??????????????????????????
    Method m = c.getMethod("whizzy", int.class, int.class);??
    boolean unstable = m.isAnnotationPresent(Unstable.class);

    isAnnotationPresent()方法對(duì)于檢查marker annotation是十分有用的,因?yàn)閙arker annotation沒(méi)有成員變量,所以我們只要知道class的方法是否使用了annotation修飾就可以了。而當(dāng)處理具有成員的annotation時(shí),我們通過(guò)使用getAnnotation()方法來(lái)獲得annotation的成員信息(成員名稱(chēng)、成員值)。這里我們看到了一套優(yōu)美的java annotation系統(tǒng):如果annotation存在,那么實(shí)現(xiàn)了相應(yīng)的annotation類(lèi)型接口的對(duì)象將被getAnnotation()方法返回,接著調(diào)用定義在annotation類(lèi)型中的成員方法可以方便地獲得任何成員值。

    回想一下,前面介紹的@Reviews annotation,如果這個(gè)annotation類(lèi)型被聲明為runtime retention的話,我們通過(guò)下面的代碼來(lái)訪問(wèn)@Reviews annotation的成員值:

    AnnotatedElement target = WhizzBangClass.class; //獲得被查詢的AnnotatedElement
    // 查詢AnnotatedElement的@Reviews annotation信息
    Reviews annotation = target.getAnnotation(Reviews.class);
    // 因?yàn)?#64;Reviews annotation類(lèi)型的成員為@Review annotation類(lèi)型的數(shù)組,
    // 所以下面聲明了Review[] reviews保存@Reviews annotation類(lèi)型的value成員值。
    Review[] reviews = annotation.value();
    // 查詢每個(gè)@Review annotation的成員信息
    for(Review r : reviews) {
    ????Review.Grade grade = r.grade();
    ????String reviewer = r.reviewer();
    ????String comment = r.comment();
    ????System.out.printf("%s assigned a grade of %s and comment '%s'%n",
    ??????????????????????reviewer, grade, comment);
    }


    四、如何自定義Annotation?

    1.詳解annotation與接口的異同:
    因?yàn)閍nnotation類(lèi)型是一個(gè)非凡的接口,所以兩者之間存在著某些差異:

    A.Annotation類(lèi)型使用關(guān)鍵字@interface而不是interface。
    這個(gè)關(guān)鍵字聲明隱含了一個(gè)信息:它是繼承了java.lang.annotation.Annotation接口,并非聲明了一個(gè)interface。

    B.Annotation類(lèi)型、方法定義是獨(dú)特的、受限制的。
    Annotation類(lèi)型的方法必須聲明為無(wú)參數(shù)、無(wú)異常拋出的。這些方法定義了annotation的成員:方法名成為了成員名,而方法返回值成為了成員的類(lèi)型。而方法返回值類(lèi)型必須為primitive類(lèi)型、Class類(lèi)型、枚舉類(lèi)型、annotation類(lèi)型或者由前面類(lèi)型之一作為元素的一維數(shù)組。方法的后面可以使用default和一個(gè)默認(rèn)數(shù)值來(lái)聲明成員的默認(rèn)值,null不能作為成員默認(rèn)值,這與我們?cè)诜莂nnotation類(lèi)型中定義方法有很大不同。
    Annotation類(lèi)型和它的方法不能使用annotation類(lèi)型的參數(shù)、成員不能是generic。只有返回值類(lèi)型是Class的方法可以在annotation類(lèi)型中使用generic,因?yàn)榇朔椒軌蛴妙?lèi)轉(zhuǎn)換將各種類(lèi)型轉(zhuǎn)換為Class。

    C.Annotation類(lèi)型又與接口有著近似之處。
    它們可以定義常量、靜態(tài)成員類(lèi)型(比如枚舉類(lèi)型定義)。Annotation類(lèi)型也可以如接口一般被實(shí)現(xiàn)或者繼承。

    2.實(shí)例:
    下面,我們將看到如何定義annotation類(lèi)型的example。它展示了annotation類(lèi)型聲明以及@interface與interface之間的不同:

    package com.davidflanagan.annotations;
    import java.lang.annotation.*;

    /**
    * 使用annotation來(lái)描述那些被標(biāo)注的成員是不穩(wěn)定的,需要更改
    */
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Unstable {}

    下面的另一個(gè)example只定義了一個(gè)成員。并通過(guò)將這個(gè)成員命名為value,使我們可以方便的使用這種annotation的快捷聲明方式:

    /**
    * 使用Author這個(gè)annotation定義在程序中指出代碼的作者
    */
    public @interface Author {
    ????/** 返回作者名 */
    ????String value();
    }

    以下的example更加復(fù)雜。Reviews annotation類(lèi)型只有一個(gè)成員,但是這個(gè)成員的類(lèi)型是復(fù)雜的:由Review annotation組成的數(shù)組。Review annotation類(lèi)型有3個(gè)成員:枚舉類(lèi)型成員grade、表示Review名稱(chēng)的字符串類(lèi)型成員Reviewer、具有默認(rèn)值的字符串類(lèi)型成員Comment。

    import java.lang.annotation.*;
    ????????
    /**
    * Reviews annotation類(lèi)型只有一個(gè)成員,
    * 但是這個(gè)成員的類(lèi)型是復(fù)雜的:由Review annotation組成的數(shù)組
    */
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Reviews {
    ????Review[] value();
    }

    /**
    * Review annotation類(lèi)型有3個(gè)成員:
    * 枚舉類(lèi)型成員grade、
    ??* 表示Review名稱(chēng)的字符串類(lèi)型成員Reviewer、
    ??* 具有默認(rèn)值的字符串類(lèi)型成員Comment。
    */
    public @interface Review {
    ????// 內(nèi)嵌的枚舉類(lèi)型
    ????public static enum Grade { EXCELLENT, SATISFACTORY, UNSATISFACTORY };

    ????// 下面的方法定義了annotation的成員
    ????Grade grade();????????????????
    ????String reviewer();??????????
    ????String comment() default "";??
    }

    最后,我們來(lái)定義一個(gè)annotation方法用于羅列出類(lèi)運(yùn)行中所有的unchecked異常(上文已經(jīng)提到這種情況不一定是錯(cuò)誤)。這個(gè)annotation類(lèi)型將一個(gè)數(shù)組作為了唯一的成員。數(shù)組中的每個(gè)元素都是異常類(lèi)。為了加強(qiáng)對(duì)未檢查的異常(此類(lèi)異常都是在運(yùn)行時(shí)拋出)進(jìn)行報(bào)告,我們可以在代碼中對(duì)異常的類(lèi)型進(jìn)行限制:

    public @interface UncheckedExceptions {
    ????Class<? extends RuntimeException>[] value();
    }


    五、Meta-Annotation

    Annotation類(lèi)型可以被它們自己所標(biāo)注。Java5.0定義了4個(gè)標(biāo)準(zhǔn)的meta-annotation類(lèi)型,它們被用來(lái)提供對(duì)其它annotation類(lèi)型作說(shuō)明。這些類(lèi)型和它們所支持的類(lèi)在java.lang.annotation包中可以找到。如果需要更詳細(xì)的信息可以參考jdk5.0手冊(cè)。

    1.再談Target
    作為meta-annotation類(lèi)型的Target,它描述了annotation所修飾的程序成員的類(lèi)型。當(dāng)一個(gè)annotation類(lèi)型沒(méi)有Target時(shí),它將被作為普通的annotation看待。當(dāng)將它修飾一個(gè)特定的程序成員時(shí),它將發(fā)揮其應(yīng)用的作用,例如:Override用于修飾方法時(shí),增加了@Target這個(gè)meta-annotation就使編譯器對(duì)annotation作檢查,從而去掉修飾錯(cuò)誤類(lèi)型的Override。

    Target meta-annotation類(lèi)型有唯一的value作為成員。這個(gè)成員的類(lèi)型是java.lang.annotation.ElementType[]類(lèi)型的,ElementType類(lèi)型是可以被標(biāo)注的程序成員的枚舉類(lèi)型。

    2.Retention的用法
    我們?cè)谖恼碌拈_(kāi)頭曾經(jīng)提到過(guò)Retention,但是沒(méi)有詳細(xì)講解。Retention描述了annotation是否被編譯器丟棄或者保留在class文件;如果保留在class文件中,是否在class文件被裝載時(shí)被虛擬機(jī)讀取。默認(rèn)情況下,annotation被保存在class文件中,但在運(yùn)行時(shí)并不能被反射訪問(wèn)。Retention具有三個(gè)取值:source、class、runtime,這些取值來(lái)自java.lang.annotation.RetentionPolicy的枚舉類(lèi)型值。

    Retention meta-annotation類(lèi)型有唯一的value作為成員,它的取值來(lái)自java.lang.annotation.RetentionPolicy的枚舉類(lèi)型值。

    3.Documented
    Documented是一個(gè)meta-annotation類(lèi)型,用于描述其它類(lèi)型的annotation應(yīng)該被作為被標(biāo)注的程序成員的公共API,因此可以被例如javadoc此類(lèi)的工具文檔化。

    Documented是一個(gè)marker annotation,沒(méi)有成員。

    4.Inherited
    @Inherited meta-annotation也是一個(gè)marker annotation,它闡述了某個(gè)被標(biāo)注的類(lèi)型是被繼承的。如果一個(gè)使用了@Inherited修飾的annotation類(lèi)型被用于一個(gè)class,則這個(gè)annotation將被用于該class的子類(lèi)。

    注意:@Inherited annotation類(lèi)型是被標(biāo)注過(guò)的class的子類(lèi)所繼承。類(lèi)并不從它所實(shí)現(xiàn)的接口繼承annotation,方法并不從它所重載的方法繼承annotation。

    值得思考的是,當(dāng)@Inherited annotation類(lèi)型標(biāo)注的annotation的Retention是RetentionPolicy.RUNTIME,則反射API增強(qiáng)了這種繼承性。如果我們使用java.lang.reflect去查詢一個(gè)@Inherited annotation類(lèi)型的annotation時(shí),反射代碼檢查將展開(kāi)工作:檢查class和其父類(lèi),直到發(fā)現(xiàn)指定的annotation類(lèi)型被發(fā)現(xiàn),或者到達(dá)類(lèi)繼承結(jié)構(gòu)的頂層。


    作者 cleverpig(http://blog.matrix.org.cn/page/cleverpig)

    posted on 2006-04-20 15:30 zeroone0 閱讀(873) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): 編程

    主站蜘蛛池模板: 一级一黄在线观看视频免费| 午夜精品一区二区三区免费视频| 久久久久久国产精品免费无码 | 亚洲高清在线观看| 57pao国产成视频免费播放| 亚洲日韩精品A∨片无码| caoporm超免费公开视频| 亚洲一区二区三区夜色| 国产网站在线免费观看| 亚洲国产aⅴ成人精品无吗| 亚洲色偷偷偷鲁综合| 永久免费AV无码国产网站| 亚洲一级特黄特黄的大片| 亚洲无码高清在线观看| 亚洲成人免费电影| 国产JIZZ中国JIZZ免费看| 亚洲娇小性xxxx| 国产AV无码专区亚洲AV手机麻豆 | 丁香花免费完整高清观看 | 亚洲A∨无码无在线观看| 午夜成人免费视频| 久久久久久国产精品免费免费男同| 亚洲va久久久噜噜噜久久 | 最近最好的中文字幕2019免费| 亚洲AV无码精品蜜桃| 岛国av无码免费无禁网站| 久久久久久毛片免费看| 亚洲一区二区三区免费在线观看| 成人毛片18女人毛片免费| 免费在线看黄的网站| 免费人成再在线观看网站 | 亚洲无码高清在线观看| 破了亲妺妺的处免费视频国产| 麻豆91免费视频| 亚洲精品国产精品乱码不卡√| 日韩免费无码一区二区三区 | 久久久久亚洲AV无码麻豆| 日韩不卡免费视频| 亚洲欧美日韩一区二区三区| 亚洲天堂一区二区| 亚洲免费观看视频|