每個標註都會有個標註型態(Annotation type),所有標註型態其實都是 java.lang.annotation.Annotation
子介面,@Override
的標註型態為java.lang.Override
,@Deprecated
的標註型態為java.lang.Deprecated
等,常用標準標註 中介紹的標註型態,都位於java.lang
套件中。
你可以自訂標註,先來看看如何定義標示標註(Marker Annotation),也就是標註名稱本身就是資訊,對編譯器或應用程式來說,主要是檢查是否有標註出現,並作出對應的動作,例如@Override
的作用就是標示標註。要定義一個標註可以使用@interface
。例如:
package cc.openhome;
public @interface Test {}
編譯完成後,就可以在程式碼中使用@Test
標註了。例如:
public class SomeTestCase {
@Test
public void testDoSome() {
...略
}
}
如果標註名稱本身無法提供足夠資訊,可進一步設定單值標註(Single-value Annotation)。例如:
package cc.openhome;
public @interface Test2 {
int timeout();
}
這表示標註將會有個timeout
屬性可以設定int
值。例如:
@Test2(timeout = 10)
public void testDoSome2() {
...
}
標註屬性也可以用陣列形式指定。例如如下定義標註的話:
package cc.openhome;
public @interface Test3 {
String[] args();
}
就可以用陣列形式指定屬性:
@Test3(args = {"arg1", "arg2"})
public void testDoSome3() {
...
}
在定義標註屬性時,如果屬性名稱為value
,則可以省略屬性名稱,直接指定值。例如:
package cc.openhome;
public @interface Ignore {
String value();
}
這個標註可以使用@Ignore(value = "message")
指定,也可以使用@Ignore("message")
指定,而以下這個標註:
package cc.openhome;
public @interface TestClass {
Class[] value();
}
可以使用@TestClass(value = {Some.class, Other.class})
指定,也可以使用@TestClass({Some.class, Other.class})
指定。
也可以對成員設定預設值,使用default
關鍵字即可。例如:
package cc.openhome;
public @interface Test4 {
int timeout() default 0;
String message default "";
}
如此一來,如果設定為@Test4
,則timeout
屬性預設值就是0,message
預設就是空字串,如果設定@Test4(timeout = 10, message = "逾時10秒")
,則timeout
屬性的值就是10,message
就是"逾時10秒"。如果是Class
設定的屬性比較特別,default
之後不能接上null
,會發生編譯錯誤,必須自訂一個類別作為預設值。例如:
package cc.openhome;
public @interface Test5 {
Class expected() default Default.class;
class Default {}
}
如果要設定陣列預設值的話,可以在default
之後加上{}
。例如:
package cc.openhome;
public @interface Test6 {
String[] args() default {};
}
必要時{}
中可放置元素值。例如:
package cc.openhome;
public @interface Test7 {
String[] args() default {"arg1", "arg2"};
}
在定義標註時,可使用java.lang.annotation.Target
限定標註使用位置,限定時可指定java.lang.annotation.ElementType
的列舉值:
package java.lang.annotation;
public enum ElementType {
TYPE, // 用於類別、介面、列舉等
FIELD, // 用於資料成員
METHOD, // 用於方法
PARAMETER, // 用於方法上的參數
CONSTRUCTOR, // 用於建構式
LOCAL_VARIABLE, // 用於區域變數
ANNOTATION_TYPE, // 用於標註型態
PACKAGE, // 適用套件
TYPE_PARAMETER, // 用於泛型宣告,JDK8新增
TYPE_USE // 用於各種型態,JDK8新增
}
例如想將@Test8
限定只能用於方法:
package cc.openhome;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
@Target({ElementType.METHOD})
public @interface Test8 {}
嘗試在方法以外的地方加上@Test8
就會發生編譯錯誤:
在製作JavaDoc文件時,預設並不會將標註資料加入文件中,如果想要將標註資料加入文件,可以使用
java.lang.annotation.Documented
。例如:package cc.openhome;
import java.lang.annotation.Documented;
@Documented
public @interface Test9 {}
如果在文件中使用到@Test9
,則產生JavaDoc後,文件中就會包括@Test9
的資訊:
在定義標註型態並使用於程式碼時,預設父類別設定的標註,不會被繼承至子類別,可以在定義標註時設定
java.lang.annotation.Inherited
標註,就可以讓標註被子類別繼承。例如:package cc.openhome;
import java.lang.annotation.Inherited;
@Inherited
public @interface Test10 {}
JDK8新增了型態標註(Type Annotations),可以讓你在有型態資訊出現的任何位置進行標註,詳情可參考 Type Annotations Specification,主要就是在
ElementType
增加了TYPE_PARAMETER
與TYPE_USE
兩個列舉值。舉例而言,如果你想如下標註:List<@Email String> emails = ...;
那麼在定義
Email
標註時,@Target
可以使用ElementType.TYPE_USE
。例如:package cc.openhome;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
@Target({ElementType.TYPE_USE})
public @interface Email {}