簡介: 是什么, 用途, 為什么有用

Annotation 是一種元數(shù)據(jù)(metadata),它本身不是代碼,但提供了一些關(guān)于代碼的數(shù)據(jù)。這些數(shù)據(jù)可以是限定代碼的一些功能,也可以是增加一些功能??梢詫⑺斫鉃榇a的修飾符,將代碼當(dāng)作一個(gè)名詞,則Annotation是一個(gè)形容詞,它使這個(gè)名詞更加準(zhǔn)確、或限定為更小的范圍。如紅紅的蘋果,直接說蘋果也行,但有了紅紅的這個(gè)形容詞,就更加準(zhǔn)確了。 Annotation可以向編譯器提供一些信息,如檢測錯誤(@Override)、消除編譯告警(@SuppressWarning)。 編譯期生成代碼(@Junit)。運(yùn)行期提供一些檢查機(jī)制(@NonNull)。被框架廣泛使用。 和comment一樣,annotation也能夠起到描述代碼功能。但它還具有檢查機(jī)制。 為什么是需要的?有些信息無法通過代碼表達(dá)出來,此時(shí)通過Annotation可以很好的達(dá)到。

使用: 一個(gè)簡單例子,如@Override。系統(tǒng)提供的annotation,做成列表

Java 預(yù)定義的annotation

namedescription
@Deprecated被修飾的元素已經(jīng)被棄用,不應(yīng)該再使用。編譯器會打印一個(gè)告警,如果這個(gè)元素還被使用
@Override被修飾元素會覆蓋基類的定義。
@SuppressWarnings消除一個(gè)編譯告警。接受一個(gè)參數(shù)
@SafeVarargs指明方法不會對varargs做不安全的操作。unchecked 告警會被抑制
@FunctionalInterface被用作為函數(shù)式接口,java8引入

修飾其它annotation的annotation

@Retention參數(shù): RetentionPolicy.SOURCE, .CLASS, .RUNTIME. 表示這個(gè)annotation 會被保存的地方
@Documented元素必須被javadoc文檔化
@Target參數(shù):ElementType.METHOD, .ANNOTATION_TYPE, .CONSTRUCTOR, .FIELD, .LOCAL_VARIABLE,

.PACKAGE, .PARAMETER, .TYPE. 指定元素的類型。
@Inherited這個(gè)annotation會被它修改的類型的子類繼承
@Repeatable可被多次應(yīng)用在一個(gè)元素上

REF: https://docs.oracle.com/javase/tutorial/java/annotations/predefined.html

使用方法,以@Override為例。

class A {    void foo(){
        System.out.println("Supper class");
    }
}class A1 extends A {    @Override
    void foo(){
        System.out.println("Sub class");
    }
}class Test {    public static void main(String[] args) {        A a = new A1();
        a.foo();
    }
}

@Override確保被修飾方法確實(shí)是覆蓋了一個(gè)基類的方法,而不是定義了一個(gè)新的方法(這種情況在方法名拼寫錯誤時(shí)發(fā)生),或重載了一個(gè)方法(在參數(shù)類型錯誤時(shí)發(fā)生)。以下是一個(gè)方法名拼寫錯誤的例子。原本想覆蓋基類的foo方法,但卻拼寫錯了,導(dǎo)致程序結(jié)果錯誤。通過@Override在編譯器就可檢測出來。

class A {    void foo(){
        System.out.println("Supper class");
    }
}class A1 extends A {    void fooo(){
        System.out.println("Sub class");
    }
}class Test {    public static void main(String[] args) {        A a = new A1();
        a.foo();
    }
}

定義新的annotation。 涉及到的語法, processor的編寫(以及使用APT輔助編寫)。

定義一個(gè)新的annotation包括兩部分,annotation本身的定義,和 annotation processor的定義。 其中annotation本身的定義相當(dāng)于給系統(tǒng)增加了一個(gè)annotaion類型。 annotation processor是來解析、處理這個(gè)annotation. 一個(gè)定義annotation的例子:

定義了一個(gè)名為Autocall的annotation,它有一個(gè)屬性 msg(默認(rèn)值為"")。@Rentention 說明它會在RUNTIME時(shí)使用,@Target說明它只能修飾方法。

使用這個(gè)annotaion,修飾Test的foo方法:

class Test {    @Autocall
    public static foo() {

    }    public static void main(String[] args) {
    }
}

這個(gè)annotation設(shè)計(jì)的作用是使被修飾方法自動被main函數(shù)調(diào)用。這個(gè)需要新增代碼來實(shí)現(xiàn),也即為這個(gè)annotation編寫一個(gè)processor來實(shí)現(xiàn)。如果沒有processor, annotation和comment沒什么區(qū)別。

Annotation processor的例子:

import java.lang.annotation.*;import java.lang.Class;import java.lang.reflect.Method;@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)@interface Autocall {    String msg() default "";
}class AutocallProcessor {    static void process(Class cls) {        try{            Class anntCls= Class.forName("Autocall");            for(Method mtd: cls.getDeclaredMethods()){                Autocall annt = (Autocall)mtd.getDeclaredAnnotation(anntCls);                if (annt != null) {                    // call this Method.
                    try{
                        mtd.invoke(null);
                    } catch(Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        } catch (ClassNotFoundException e){
            System.out.println("ClassNotFoundException");
            System.exit(1);
        }

    }    @Autocall
    static void foo(){
        System.out.println("Foo called");
    }    public static void main(String[] args) {        try{
            process(Class.forName("AutocallProcessor"));
        } catch (ClassNotFoundException e) {
            e.printStackTrace();

        }
    }