閱讀205 返回首頁    go 技術社區[雲棲]


Java注解全麵解析

1.基本語法

注解定義看起來很像接口的定義。事實上,與其他任何接口一樣,注解也將會編譯成class文件。

@Target(ElementType.Method)

@Retention(RetentionPolicy.RUNTIME)

public @interface Test {}

除了@符號以外,@Test的定義很像一個空的接口。定義注解時,需要一些元注解(meta-annotation),如@Target和@Retention

@Target用來定義注解將應用於什麼地方(如一個方法或者一個域)

@Retention用來定義注解在哪一個級別可用,在源代碼中(source),類文件中(class)或者運行時(runtime)

在注解中,一般都會包含一些元素以表示某些值。當分析處理注解時,程序可以利用這些值。沒有元素的注解稱為標記注解(marker annotation)

四種元注解,元注解專職負責注解其他的注解,所以這四種注解的Target值都是ElementType.ANNOTATION_TYPE

注解 說明
@Target 表示該注解可以用在什麼地方,由ElementType枚舉定義
CONSTRUCTOR:構造器的聲明
FIELD:域聲明(包括enum實例)
LOCAL_VARIABLE:局部變量聲明
METHOD:方法聲明
PACKAGE:包聲明
PARAMETER:參數聲明
TYPE:類、接口(包括注解類型)或enum聲明
ANNOTATION_TYPE:注解聲明(應用於另一個注解上)
TYPE_PARAMETER:類型參數聲明(1.8新加入)
TYPE_USE:類型使用聲明(1.8新加入)
PS:當注解未指定Target值時,此注解可以使用任何元素之上,就是上麵的類型
@Retention 表示需要在什麼級別保存該注解信息,由RetentionPolicy枚舉定義
SOURCE:注解將被編譯器丟棄(該類型的注解信息隻會保留在源碼裏,源碼經過編譯後,注解信息會被丟棄,不會保留在編譯好的class文件裏)
CLASS:注解在class文件中可用,但會被VM丟棄(該類型的注解信息會保留在源碼裏和class文件裏,在執行的時候,不會加載到虛擬機(JVM)中)
RUNTIME:VM將在運行期也保留注解信息,因此可以通過反射機製讀取注解的信息(源碼、class文件和執行的時候都有注解的信息)
PS:當注解未定義Retention值時,默認值是CLASS
@Documented 表示注解會被包含在javaapi文檔中
@Inherited 允許子類繼承父類的注解

2. 注解元素

– 注解元素可用的類型如下:

– 所有基本類型(int,float,boolean,byte,double,char,long,short)

– String

– Class

– enum

– Annotation

– 以上類型的數組

如果使用了其他類型,那編譯器就會報錯。也不允許使用任何包裝類型。注解也可以作為元素的類型,也就是注解可以嵌套。

元素的修飾符,隻能用public或default。

– 默認值限製

編譯器對元素的默認值有些過分挑剔。首先,元素不能有不確定的值。也就是說,元素必須要麼具有默認值,要麼在使用注解時提供元素的值。

其次,對於非基本類型的元素,無論是在源代碼中聲明,還是在注解接口中定義默認值,都不能以null作為值。這就是限製,這就造成處理器很難表現一個元素的存在或缺失狀態,因為每個注解的聲明中,所有的元素都存在,並且都具有相應的值。為了繞開這個限製,隻能定義一些特殊的值,例如空字符串或負數,表示某個元素不存在。

@Target(ElementType.Method)

@Retention(RetentionPolicy.RUNTIME)

public @interface MockNull {

public int id() default -1;

public String description() default “”;

}

3. 快捷方式

何為快捷方式呢?先來看下springMVC中的Controller注解

@Target({ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Component

public @interface Controller {

String value() default “”;

}

可以看見Target應用於類、接口、注解和枚舉上,Retention策略為RUNTIME運行時期,有一個String類型的value元素。平常使用的時候基本都是這樣的:

@Controller(“/your/path”)

public class MockController { }

這就是快捷方式,省略了名-值對的這種語法。下麵給出詳細解釋:

注解中定義了名為value的元素,並且在應用該注解的時候,如果該元素是唯一需要賦值的一個元素,那麼此時無需使用名-值對的這種語法,而隻需在括號內給出value元素所需的值即可。這可以應用於任何合法類型的元素,當然了,這限製了元素名必須為value。

4. JDK1.8注解增強

TYPE_PARAMETER和TYPE_USE

在JDK1.8中ElementType多了兩個枚舉成員,TYPE_PARAMETER和TYPE_USE,他們都是用來限定哪個類型可以進行注解。舉例來說,如果想要對泛型的類型參數進行注解:

public class AnnotationTypeParameter<@TestTypeParam T> {}

那麼,在定義@TestTypeParam時,必須在@Target設置ElementType.TYPE_PARAMETER,表示這個注解可以用來標注類型參數。例如:

@Target(ElementType.TYPE_PARAMETER)

@Retention(RetentionPolicy.RUNTIME)

public @interface TestTypeParam {}

ElementType.TYPE_USE用於標注各種類型,因此上麵的例子也可以將TYPE_PARAMETER改為TYPE_USE,一個注解被設置為TYPE_USE,隻要是類型名稱,都可以進行注解。例如有如下注解定義:

@Target(ElementType.TYPE_USE)

@Retention(RetentionPolicy.RUNTIME)

public @interface Test {}

那麼以下的使用注解都是可以的:

List<@Test Comparable> list1 = new ArrayList<>();

List<? extends Comparable> list2 = new ArrayList<@Test Comparable>();

@Test String text;

text = (@Test String)new Object();

java.util. @Test Scanner console;

console = new java.util.@Test Scanner(System.in);

PS:以上@Test注解都是在類型的右邊,要注意區分1.8之前的枚舉成員,例如:

@Test java.lang.String text;

在上麵這個例子中,顯然是在進行text變量標注,所以還使用當前的@Target會編譯錯誤,應該加上ElementType.LOCAL_VARIABLE。

@Repeatable注解

@Repeatable注解是JDK1.8新加入的,從名字意思就可以大概猜出他的意思(可重複的)。可以在同一個位置重複相同的注解。舉例:

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

public @interface Filter {

String [] value();

}

如下進行注解使用:

@Filter({“/admin”,”/main”})

public class MainFilter { }

換一種風格:

@Filter(“/admin”)

@Filter(“/main”)

public class MainFilter {}

在JDK1.8還沒出現之前,沒有辦法到達這種“風格”,使用1.8,可以如下定義@Filter:

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Repeatable(Filters.class)

public @interface Filter {

String  value();

}

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

public @interface Filters {

Filter [] value();

}

實際上這是編譯器的優化,使用@Repeatable時告訴編譯器,使用@Filters來作為收集重複注解的容器,而每個@Filter存儲各自指定的字符串值。

JDK1.8在AnnotatedElement接口新增了getDeclaredAnnotationsByType和getAnnotationsByType,在指定@Repeatable的注解時,會尋找重複注解的容器中。相對於,getDeclaredAnnotation和getAnnotation就不會處理@Repeatable注解。舉例如下:

@Filter(“/admin”)

@Filter(“/filter”)

public class FilterClass {

public static void main(String[] args) {

Class<FilterClass> filterClassClass = FilterClass.class;

Filter[] annotationsByType = filterClassClass.getAnnotationsByType(Filter.class);

if (annotationsByType != null) {

for (Filter filter : annotationsByType) {

System.out.println(filter.value());

}

}

System.out.println(filterClassClass.getAnnotation(Filter.class));

}

}

日誌如下:

/admin

/filter

null

最後更新:2017-05-19 17:32:17

  上一篇:go  Yahoo的流計算引擎基準測試
  下一篇:go  《Spark 官方文檔》