什么是注解
注解對于開發(fā)人員來講既熟悉又陌生滚澜,熟悉是因為只要你是做開發(fā),都會用到注解(常見的@Override)嫁怀;陌生是因為即使不使用注解也照常能夠進行開發(fā)设捐;注解不是必須的,但了解注解有助于我們深入理解某些第三方框架(比如Android Support Annotations塘淑、JUnit萝招、xUtils、ActiveAndroid等)存捺,提高工作效率槐沼。
Java注解又稱為標注,是Java從1.5開始支持加入源碼的特殊語法元數(shù)據(jù)捌治;Java中的類岗钩、方法、變量肖油、參數(shù)凹嘲、包都可以被注解。這里提到的元數(shù)據(jù)是描述數(shù)據(jù)的數(shù)據(jù),結(jié)合實例來說明:
<string name="app_name">AnnotionDemo</string>
這里的"app_name"就是描述數(shù)據(jù)"AnnotionDemo"的數(shù)據(jù),這是在配置文件中寫的宛畦,注解是在源碼中寫的次和,如下所示:
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_layout);
new Thread(new Runnable(){
@Override
public void run(){
setTextInOtherThread();
}
}).start();
}
在上面的代碼中,在MainActivity.java中復寫了父類Activity.java的onCreate方法诉探,使用到了@Override注解。但即使不加上@Override注解標記代碼阳液,程序也能夠正常運行。那這里的@Override注解有什么用呢曹动?使用它有什么好處第献?事實上科侈,@Override是告訴編譯器這個方法是一個重寫方法蔫慧,如果父類中不存在該方法橄仍,編譯器會報錯,提示該方法不是父類中的方法如孝。如果不小心拼寫錯誤宪哩,將onCreate寫成了onCreat,而且沒有使用@Override注解第晰,程序依然能夠編譯通過锁孟,但運行結(jié)果和期望的大不相同。從示例可以看出茁瘦,注解有助于閱讀代碼品抽。
使用注解很簡單,根據(jù)注解類的@Target所修飾的對象范圍甜熔,可以在類圆恤、方法、變量腔稀、參數(shù)盆昙、包中使用“@+注解類名+[屬性值]”的方式使用注解羽历。比如:
@UiThread
private void setTextInOtherThread(@StringRes int resId){
TextView threadTxtView = (TextView)MainActivity.this.findViewById(R.id.threadTxtViewId);
threadTxtView.setText(resId);
}
特別說明:
注解僅僅是元數(shù)據(jù),和業(yè)務邏輯無關淡喜,所以當你查看注解類時窄陡,發(fā)現(xiàn)里面沒有任何邏輯處理;
javadoc中的@author拆火、@version、@param涂圆、@return们镜、@deprecated、@hide润歉、@throws模狭、@exception、@see是標記踩衩,并不是注解嚼鹉;
注解的作用
格式檢查:告訴編譯器信息,比如被@Override標記的方法如果不是父類的某個方法驱富,IDE會報錯锚赤;
減少配置:運行時動態(tài)處理,得到注解信息褐鸥,實現(xiàn)代替配置文件的功能线脚;
減少重復工作:比如第三方框架xUtils,通過注解@ViewInject減少對findViewById的調(diào)用叫榕,類似的還有(JUnit浑侥、ActiveAndroid等);
注解是如何工作的晰绎?
注解僅僅是元數(shù)據(jù)寓落,和業(yè)務邏輯無關,所以當你查看注解類時荞下,發(fā)現(xiàn)里面沒有任何邏輯處理伶选,eg:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewInject {
int value();
/* parent view id */
int parentId() default 0;
}
如果注解不包含業(yè)務邏輯處理,必然有人來實現(xiàn)這些邏輯锄弱。注解的邏輯實現(xiàn)是元數(shù)據(jù)的用戶來處理的考蕾,注解僅僅提供它定義的屬性(類/方法/變量/參數(shù)/包)的信息,注解的用戶來讀取這些信息并實現(xiàn)必要的邏輯会宪。當使用java中的注解時(比如@Override肖卧、@Deprecated、@SuppressWarnings)JVM就是用戶掸鹅,它在字節(jié)碼層面工作塞帐。如果是自定義的注解拦赠,比如第三方框架ActiveAndroid,它的用戶是每個使用注解的類葵姥,所有使用注解的類都需要繼承Model.java荷鼠,在Model.java的構(gòu)造方法中通過反射來獲取注解類中的每個屬性:
public TableInfo(Class<? extends Model> type) {
mType = type;
final Table tableAnnotation = type.getAnnotation(Table.class);
if (tableAnnotation != null) {
mTableName = tableAnnotation.name();
mIdName = tableAnnotation.id();
}
else {
mTableName = type.getSimpleName();
}
// Manually add the id column since it is not declared like the other columns.
Field idField = getIdField(type);
mColumnNames.put(idField, mIdName);
List<Field> fields = new LinkedList<Field>(ReflectionUtils.getDeclaredColumnFields(type));
Collections.reverse(fields);
for (Field field : fields) {
if (field.isAnnotationPresent(Column.class)) {
final Column columnAnnotation = field.getAnnotation(Column.class);
String columnName = columnAnnotation.name();
if (TextUtils.isEmpty(columnName)) {
columnName = field.getName();
}
mColumnNames.put(field, columnName);
}
}
}
注解和配置文件的區(qū)別
通過上面的描述可以發(fā)現(xiàn),其實注解干的很多事情榔幸,通過配置文件也可以干允乐,比如為類設置配置屬性;但注解和配置文件是有很多區(qū)別的削咆,在實際編程過程中牍疏,注解和配置文件配合使用在工作效率、低耦合拨齐、可拓展性方面才會達到權(quán)衡鳞陨。
配置文件:
使用場合:
外部依賴的配置,比如build.gradle中的依賴配置瞻惋;
同一項目團隊內(nèi)部達成一致的時候厦滤;
非代碼類的資源文件(比如圖片、布局歼狼、數(shù)據(jù)掏导、簽名文件等);
優(yōu)點:
降低耦合蹂匹,配置集中碘菜,容易擴展,比如Android應用多語言支持限寞;
對象之間的關系一目了然忍啸,比如strings.xml;
xml配置文件比注解功能齊全履植,支持的類型更多计雌,比如drawable、style等玫霎;
缺點:
繁瑣凿滤;
類型不安全,比如R.java中的都是資源ID庶近,用TextView的setText方法時傳入int值時無法檢測出該值是否為資源ID翁脆,但@StringRes可以;
注解:
使用場合:
動態(tài)配置信息鼻种;
代為實現(xiàn)程序邏輯(比如xUtils中的@ViewInject代為實現(xiàn)findViewById)反番;
代碼格式檢查,比如Override、Deprecated罢缸、NonNull篙贸、StringRes等,便于IDE能夠檢查出代碼錯誤枫疆;
優(yōu)點:
在class文件中爵川,提高程序的內(nèi)聚性;
減少重復工作息楔,提高開發(fā)效率寝贡,比如findViewById。
缺點:
如果對annotation進行修改值依,需要重新編譯整個工程兔甘;
業(yè)務類之間的關系不如XML配置那樣一目了然;
程序中過多的annotation鳞滨,對于代碼的簡潔度有一定影響;
擴展性較差蟆淀;
常見注解
API
Android開發(fā)過程中使用到的注解主要來自如下幾個地方:
Android SDK:在包android.annotation下拯啦;
Android Annotation Support包:在包android.support.annotation下;
JDK:在包java.lang下熔任;
第三方框架中的自定義注解褒链;
最常見注解
@Override
屬于標記注解,不需要設置屬性值疑苔;只能添加在方法的前面甫匹,用于標記該方法是復寫的父類中的某個方法,如果在父類沒有的方法前面加上@Override注解惦费,編譯器會報錯:
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
@Deprecated
&emsp兵迅;屬于標記注解,不需要設置屬性值薪贫;可以對構(gòu)造方法恍箭、變量、方法瞧省、包扯夭、參數(shù)標記,告知用戶和編譯器被標記的內(nèi)容已不建議被使用鞍匾,如果被使用交洗,編譯器會報警告,但不會報錯橡淑,程序也能正常運行:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.METHOD, ElementType.PACKAGE, ElementType.PARAMETER, ElementType.TYPE})
public @interface Deprecated {
}
@SuppressWarnings
&emsp构拳;可以對構(gòu)造方法、變量、方法隐圾、包伍掀、參數(shù)標記,用于告知編譯器忽略指定的警告暇藏,不用再編譯完成后出現(xiàn)警告信息:
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.CONSTRUCTOR, ElementType.LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
@TargetApi
可以對接口蜜笤、方法、構(gòu)造方法標記盐碱,如果在應用中指定minSdkVersion為8把兔,但有地方需要使用API 11中的方法,為了避免編譯器報錯瓮顽,在調(diào)用API11中方法的接口县好、方法或者構(gòu)造方法前面加上@Target(11),這樣該方法就可以使用<=11的API接口了暖混。雖然這樣能夠避免編譯器報錯缕贡,但在運行時需要注意,不能在API低于11的設備中使用該方法拣播,否則會crash(可以獲取程序運行設備的API版本來判斷是否調(diào)用該方法):
@Target({TYPE, METHOD, CONSTRUCTOR})
@Retention(RetentionPolicy.CLASS)
public @interface TargetApi {
/**
* This sets the target api level for the type..
*/
int value();
}
@SuppressLint
和@Target的功能差不多晾咪,但使用范圍更廣,主要用于避免在lint檢查時報錯:
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.CLASS)
public @interface SuppressLint {
/**
* The set of warnings (identified by the lint issue id) that should be
* ignored by lint. It is not an error to specify an unrecognized name.
*/
String[] value();
}
Android Annotation Support包中的注解介紹:
Android support library從19.1版本開始引入了一個新的注解庫贮配,它包含很多有用的元注解谍倦,你能用它們修飾你的代碼,幫助你發(fā)現(xiàn)bug泪勒。Support library自己本身也用到了這些注解昼蛀,所以作為support library的用戶,Android Studio已經(jīng)基于這些注解校驗了你的代碼并且標注其中潛在的問題圆存。
這些注解是作為一個support包提供給開發(fā)者使用叼旋,要使用他們,需要在build.gradle中添加對android support-annotations的依賴:
compile 'com.android.support:support-annotations:22.2.0'
support包中的注解分為如下幾大類:
- Nullness注解:
@Nullable:用于標記方法參數(shù)或者返回值可以為空沦辙;
@NonNull:用于標記方法參數(shù)或者返回值不能為空送淆,如果為空編譯器會報警告;
- 資源類型注解:
這類注解主要用于標記方法的參數(shù)必須要是指定的資源類型怕轿,如果不是偷崩,IDE就會報錯;因為資源文件都是靜態(tài)的撞羽,所以在編寫代碼時IDE就知道傳值是否錯誤阐斜,可以避免傳的資源id錯誤導致運行時異常。資源類型注解包括@AnimatorRes诀紊、@AnimRes谒出、@AnyRes、@ArrayRes、@BoolRes笤喳、@ColorRes为居、@DimenRes、@DrawableRes杀狡、@FractionRes蒙畴、@IdRes、@IntgerRes呜象、@InterpolatorRes膳凝、@LayoutRes、@MenuRes恭陡、@PluralsRes蹬音、@RawRes、@StringRes休玩、@StyleableRes著淆、@StyleRes、@TransitionRes拴疤、@XmlRes牧抽。
- 類型定義注解:
這類注解用于檢查“魔幻數(shù)”,很多時候遥赚,我們使用整型常量代替枚舉類型(性能考慮),例如我們有一個IceCreamFlavourManager類阐肤,它具有三種模式的操作:VANILLA凫佛,CHOCOLATE和STRAWBERRY。我們可以定義一個名為@Flavour的新注解孕惜,并使用@IntDef指定它可以接受的值類型:
public class IceCreamFlavourManager {
private int flavour;
public static final int VANILLA = 0;
public static final int CHOCOLATE = 1;
public static final int STRAWBERRY = 2;
@IntDef({VANILLA, CHOCOLATE, STRAWBERRY})
public @interface Flavour {
}
@Flavour
public int getFlavour() {
return flavour;
}
public void setFlavour(@Flavour int flavour) {
this.flavour = flavour;
}
}
這時如果我們使用錯誤的整型值調(diào)用IceCreamFlavourManager.setFlavour時愧薛,IDE將報錯如下:
IDE甚至會提示我們可以使用的有效的取值:
我們也可以指定整型值作為標志位,也就是說這些整型值可以使用’|’或者’&’進行與或等操作衫画。如果我們把@Flavour定義為如下標志位:
@IntDef(flag = true, value = {VANILLA, CHOCOLATE, STRAWBERRY})
public @interface Flavour {
}
那么可以如下調(diào)用:
iceCreamFlavourManager.setFlavour(IceCreamFlavourManager.VANILLA & IceCreamFlavourManager.CHOCOLATE);
- 線程注解:
用于標記指定的方法毫炉、類(如果一個類中的所有方法都有相同的線程需求,就可以對這個類進行注解削罩,比如View.java就被@UIThread所標記)只能在指定的線程類中被調(diào)用瞄勾,包括:@UiThread、@MainThread弥激、@WorkerThread进陡、@BinderThread;以@UIThread為例微服,說明這類注解的使用方法:
public class MainActivity extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_layout);
new Thread(new Runnable(){
@Override
public void run(){
setTextInOtherThread(R.string.app_name);
// setTextInOtherThread2(R.string.app_name);
}
}).start();
}
@UiThread
private void setTextInOtherThread(@StringRes int resId){
TextView threadTxtView = (TextView)MainActivity.this.findViewById(R.id.threadTxtViewId);
threadTxtView.setText(resId);
}
private void setTextInOtherThread2(@StringRes final int resId){
MainActivity.this.runOnUiThread(new Runnable(){
@Override
public void run(){
TextView threadTxtView = (TextView)MainActivity.this.findViewById(R.id.threadTxtViewId);
threadTxtView.setText(resId);
}
});
}
}
@UIThread和@MainThread的區(qū)別:在進程里只有一個主線程趾疚。這個就是@MainThread。同時這個線程也是一個@UiThread。比如activity的主要窗口就運行在這個線程上糙麦。然而它也有能力為應用創(chuàng)建其他線程辛孵。這很少見,一般具備這樣功能的都是系統(tǒng)進程赡磅。通常是把和生命周期有關的用@MainThread標注魄缚,和View層級結(jié)構(gòu)相關的用@UiThread標注。但是由于@MainThread本質(zhì)上是一個@UiThread仆邓,而大部分情況下@UiThread又是一個@MainThread鲜滩,所以工具(lint ,Android Studio,等等)可以把他們互換,所以你能在一個可以調(diào)用@MainThread方法的地方也能調(diào)用@UiThread方法节值,反之亦然徙硅。
- GRB顏色值注解:
用于標記傳遞的顏色值必須是整型值,并且不能是color資源ID搞疗;當你的API期望一個顏色資源的時候嗓蘑,可以用@ColorRes標注,但是當你有一個相反的使用場景時匿乃,這種用法就不可用了桩皿,因為你并不是期望一個顏色資源id,而是一個真實的RGB或者ARGB的顏色值幢炸。在這種情況下泄隔,你可以使用@ColorInt注解,表示你期望的是一個代表顏色的整數(shù)值:
public void setTextColor(@ColorInt int color);
有了這個宛徊,當你傳遞一個顏色id而不是顏色值的時候佛嬉,lint就會標記出這段不正確的代碼:
- 值約束注解:
用于標記參數(shù)必須是指定類型的值,并且值的范圍必須在約束的范圍內(nèi)闸天,包括@Size暖呕、@IntRange、@FloatRange苞氮。如果你的參數(shù)是一個float或者double類型湾揽,并且一定要在某個范圍內(nèi),你可以使用@FloatRange注解:
public void setAlpha(@FloatRange(from=0.0, to=1.0) float alpha){
...
}
如果有人使用該API的時候傳遞一個0-255的值笼吟,比如嘗試調(diào)用setAlpha(128)库物,那么工具就會捕獲這一問題:
把這些注解應用到參數(shù)上是非常有用的,因為用戶很有可能會提供錯誤范圍的參數(shù)贷帮,比如上面的setAlpha例子艳狐,有的API是采用0-255的方式,而有的是采用0-1的float值的方式皿桑。
對于數(shù)據(jù)毫目、集合以及字符串蔬啡,你可以用@Size注解參數(shù)來限定集合的大小(當參數(shù)是字符串的時候,可以限定字符串的長度)镀虐。舉幾個例子:
1箱蟆、集合不能為空: @Size(min=1);
2刮便、字符串最大只能有23個字符: @Size(max=23)空猜;
3、數(shù)組只能有2個元素: @Size(2)恨旱;
4辈毯、數(shù)組的大小必須是2的倍數(shù) (例如圖形API中獲取位置的x/y坐標數(shù)組: @Size(multiple=2)。
- 權(quán)限注解:
如果你的方法需要調(diào)用者有特定的權(quán)限搜贤,你可以使用@RequiresPermission注解:
@RequiresPermission(Manifest.permission.SET_WALLPAPER)
public abstract void setWallpaper(Bitmap bitmap) throws IOException;
如果你至少需要權(quán)限集合中的一個谆沃,你可以使用anyOf屬性:
@RequiresPermission(anyOf = {
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION})
public abstract Location getLastKnownLocation(String provider);
如果你同時需要多個權(quán)限,你可以用allOf屬性:
@RequiresPermission(allOf = {
Manifest.permission.READ_HISTORY_BOOKMARKS,
Manifest.permission.WRITE_HISTORY_BOOKMARKS})
public static final void updateVisitedHistory(ContentResolver cr, String url, boolean real) {
對于intents的權(quán)限仪芒,可以直接在定義的intent常量字符串字段上標注權(quán)限需求(他們通常都已經(jīng)被@SdkConstant注解標注過了):
@RequiresPermission(android.Manifest.permission.BLUETOOTH)
public static final String ACTION_REQUEST_DISCOVERABLE =
"android.bluetooth.adapter.action.REQUEST_DISCOVERABLE";
對于content providers的權(quán)限唁影,你可能需要單獨的標注讀和寫的權(quán)限訪問,所以可以用@Read或者@Write標注每一個權(quán)限需求:
@RequiresPermission.Read(@RequiresPermission(READ_HISTORY_BOOKMARKS))
@RequiresPermission.Write(@RequiresPermission(WRITE_HISTORY_BOOKMARKS))
public static final Uri BOOKMARKS_URI = Uri.parse("content://browser/bookmarks");
- 復寫方法注解:
如果你的API允許使用者重寫你的方法掂名,但你又需要你自己的方法(父方法)在重寫的時候也被調(diào)用据沈,這時候你可以使用@CallSuper標注:
@CallSuper
protected void onCreate(@Nullable Bundle savedInstanceState) {
用了這個后,當重寫的方法沒有調(diào)用父方法時饺蔑,工具就會給予警告提示:
- 返回值注解:
如果你的方法有返回值锌介,你期望調(diào)用者用這個值做些事情,那么你可以使用@CheckResult注解標注這個方法猾警。
你并不需要為每個非空方法都進行標注孔祸。它主要的目的是幫助哪些容易被混淆,難以被理解的API的使用者肿嘲。
比如,可能很多開發(fā)者都對String.trim()一知半解筑公,認為調(diào)用了這個方法雳窟,就可以讓字符串改變以去掉空白字符。如果這個方法被@CheckResult標注匣屡,工具就會對那些沒有使用trim()返回結(jié)果的調(diào)用者發(fā)出警告封救。
Android中,Context#checkPermission這個方法已經(jīng)被@CheckResult標注了:
@CheckResult(suggest="#enforcePermission(String,int,int,String)")
public abstract int checkPermission(@NonNull String permission, int pid, int uid);
這是非常重要的捣作,因為有些使用context.checkPermission的開發(fā)者認為他們已經(jīng)執(zhí)行了一個權(quán)限 —-但其實這個方法僅僅只做了檢查并且反饋一個是否成功的值而已誉结。如果開發(fā)者使用了這個方法,但是又不用其返回值券躁,那么這個開發(fā)者真正想調(diào)用的可能是這個Context#enforcePermission方法惩坑,而不是checkPermission掉盅。
- 測試可見注解:
你可以把這個注解標注到類、方法或者字段上以舒,以便你在測試的時候可以使用他們趾痘。
自定義注解
通過閱讀注解類的源碼可以發(fā)現(xiàn),任何一個注解類都有如下特征:
注解類會被@interface標記蔓钟;
注解類的頂部會被@Documented永票、@Retention、@Target滥沫、@Inherited這四個注解標記(@Documented侣集、@Inherited可選,@Retention兰绣、@Target必須要有)世分;
@UiThread源碼:
@Documented
@Retention(CLASS)
@Target({METHOD,CONSTRUCTOR,TYPE})
public @interface UiThread {
}
元注解
上文提到的四個注解:@Documented、@Retention狭魂、@Target罚攀、@Inherited就是元注解,它們的作用是負責注解其它注解雌澄,主要是描述注解的一些屬性斋泄,任何注解都離不開元注解(包括元注解自身,通過元注解可以自定義注解)镐牺,元注解的用戶是JDK炫掐,JDK已經(jīng)幫助我們實現(xiàn)了這四個注解的邏輯。這四個注解在JDK的java.lang.annotation包中睬涧。對每個元注解的詳細說明如下:
- @Target:
作用:用于描述注解的使用范圍募胃,即被描述的注解可以用在什么地方;
取值:
1、CONSTRUCTOR:構(gòu)造器源葫;
2能庆、FIELD:實例;
3祷嘶、LOCAL_VARIABLE:局部變量;
4夺溢、METHOD:方法论巍;
5、PACKAGE:包风响;
6嘉汰、PARAMETER:參數(shù);
7、TYPE:類状勤、接口(包括注解類型) 或enum聲明鞋怀。
示例:
/***
*
* 實體注解接口
*/
@Target(value = {ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Entity {
/***
* 實體默認firstLevelCache屬性為false
* @return boolean
*/
boolean firstLevelCache() default false;
/***
* 實體默認secondLevelCache屬性為false
* @return boolean
*/
boolean secondLevelCache() default true;
/***
* 表名默認為空
* @return String
*/
String tableName() default "";
/***
* 默認以""分割注解
*/
String split() default "";
}
- @Retention:
作用:表示需要在什么級別保存該注解信息双泪,用于描述注解的生命周期,即被描述的注解在什么范圍內(nèi)有效接箫;
取值:
1攒读、SOURCE:在源文件中有效,即源文件保留辛友;
2薄扁、CLASS:在class文件中有效,即class保留废累;
3邓梅、RUNTIME:在運行時有效,即運行時保留邑滨;
示例:
/***
* 字段注解接口
*/
@Target(value = {ElementType.FIELD})//注解可以被添加在實例上
@Retention(value = RetentionPolicy.RUNTIME)//注解保存在JVM運行時刻,能夠在運行時刻通過反射API來獲取到注解的信息
public @interface Column {
String name();//注解的name屬性
}
- @Documented:
作用:用于描述其它類型的annotation應該被作為被標注的程序成員的公共API日缨,因此可以被例如javadoc此類的工具文檔化。
取值:它屬于標記注解掖看,沒有成員匣距;
示例:
@Documented
@Retention(CLASS)
@Target({METHOD,CONSTRUCTOR,TYPE})
public @interface UiThread {
}
- @Inherited:
作用:用于描述某個被標注的類型是可被繼承的。如果一個使用了@Inherited修飾的annotation類型被用于一個class哎壳,則這個annotation將被用于該class的子類毅待。
取值:它屬于標記注解,沒有成員归榕;
示例:
/**
* @author wangsheng
**/
@Inherited
public @interface Greeting {
public enum FontColor{ BULE,RED,GREEN};
String name();
FontColor fontColor() default FontColor.GREEN;
}
如何自定義注解
使用@interface自定義注解時尸红,自動繼承了java.lang.annotation.Annotation接口,由編譯程序自動完成其他細節(jié)刹泄。在定義注解時外里,不能繼承其他的注解或接口。@interface用來聲明一個注解特石,其中的每一個方法實際上是聲明了一個配置參數(shù)盅蝗。方法的名稱就是參數(shù)的名稱,返回值類型就是參數(shù)的類型(返回值類型只能是基本類型姆蘸、Class墩莫、String、enum)乞旦≡裟拢可以通過default來聲明參數(shù)的默認值题山。
-
自定義注解格式:
元注解 public @interface 注解名{ 定義體兰粉; }
注解參數(shù)可支持的數(shù)據(jù)類型:
1、所有基本數(shù)據(jù)類型(int,float,boolean,byte,double,char,long,short)顶瞳;
2玖姑、String類型愕秫;
3、Class類型焰络;
4戴甩、enum類型;
5闪彼、Annotation類型甜孤;
6、以上所有類型的數(shù)組畏腕。
特別說明:
1缴川、注解類中的方法只能用public或者默認這兩個訪問權(quán)修飾,不寫public就是默認描馅,eg:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitColor {
public enum Color{ BULE,RED,GREEN};
Color fruitColor() default Color.GREEN;
}
2把夸、如果注解類中只有一個成員,最好把方法名設置為"value"铭污,比如:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitName {
String value() default "";
}
3恋日、注解元素必須有確定的值,要么在定義注解的默認值中指定嘹狞,要么在使用注解時指定岂膳,非基本類型的注解元素的值不可為null。因此, 使用空字符串或0作為默認值是一種常用的做法刁绒。
- 實例演示:
ToDo.java:注解類
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface Todo {
public enum Priority {LOW, MEDIUM, HIGH}
public enum Status {STARTED, NOT_STARTED}
String author() default "Yash";
Priority priority() default Priority.LOW;
Status status() default Status.NOT_STARTED;
}
BusinessLogic:使用注解的類
public class BusinessLogic {
public BusinessLogic() {
super();
}
public void compltedMethod() {
System.out.println("This method is complete");
}
@Todo(priority = Todo.Priority.HIGH)
public void notYetStartedMethod() {
// No Code Written yet
}
@Todo(priority = Todo.Priority.MEDIUM, author = "Uday", status = Todo.Status.STARTED)
public void incompleteMethod1() {
//Some business logic is written
//But its not complete yet
}
@Todo(priority = Todo.Priority.LOW, status = Todo.Status.STARTED )
public void incompleteMethod2() {
//Some business logic is written
//But its not complete yet
}
}
TodoReport.java:解析注解信息
public class TodoReport {
public TodoReport() {
super();
}
public static void main(String[] args) {
getTodoReportForBusinessLogic();
}
/**
* 解析使用注解的類闷营,獲取通過注解設置的屬性
*/
private static void getTodoReportForBusinessLogic() {
Class businessLogicClass = BusinessLogic.class;
for(Method method : businessLogicClass.getMethods()) {
Todo todoAnnotation = (Todo)method.getAnnotation(Todo.class);
if(todoAnnotation != null) {
System.out.println(" Method Name : " + method.getName());
System.out.println(" Author : " + todoAnnotation.author());
System.out.println(" Priority : " + todoAnnotation.priority());
System.out.println(" Status : " + todoAnnotation.status());
System.out.println(" --------------------------- ");
}
}
}
}
執(zhí)行結(jié)果如下圖所示: