在Java1.5以后,引入了注解再层,也稱作元數(shù)據(jù)贸铜。作為新的特性,同時也是基礎(chǔ)知識之一树绩,我們應(yīng)該學(xué)會使用這種用法萨脑,雖然反射會帶來代碼效率問題隐轩,但相比于它的優(yōu)點饺饭,這種損失我們還是可以承受的。
元數(shù)據(jù)被定義為:描述數(shù)據(jù)的數(shù)據(jù)职车,對數(shù)據(jù)及信息資源的描述性信息瘫俊。
我們可以認(rèn)為注解的目的就是對數(shù)據(jù)添加的附加信息。在java源代碼中添加注解悴灵,有助于減輕編寫“樣板”代碼的負(fù)擔(dān)(findViewById)扛芽,更加干凈易讀的代碼以及編譯器類型檢查。
注解的語法比較簡單积瞒,使用一個@符號修飾代表的就是一個注解川尖。Java 5內(nèi)置的幾種注解有
//當(dāng)前的方法定義將覆蓋超類中的方法
@Override
//代表被這個元數(shù)據(jù)修飾的元素已被廢棄,使用已廢棄的方法或?qū)ο缶幾g器會發(fā)出警告
@Deprecated
//關(guān)閉不當(dāng)?shù)木幾g器警告信息茫孔,比如unchecked叮喳,未檢查的類型等
@SuppressWarnings
自定義注解類型
使用@interface來定義注解類型
public @interface Test {
}
使用注解類型
//一般寫法,比較優(yōu)美
@Test
void test(){
}
//注解可以看做是一種修飾符缰贝,它的使用和修飾符幾乎一模一樣
// 不太好看馍悟,不建議
public static synchronized @Test void test(){
}
上面簡單定義了一個注解,但是一般我們定義的注解剩晴,還會定義一些注解的類型锣咒,Annotation有四種元注解類型,元注解專職負(fù)責(zé)注解其他的注解赞弥,詳情可以看Java API里面的Annotation
# Retention
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
RetentionPolicy value();
}
# Target
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}
# Documented
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}
# Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}
Rentention
Rentention定義該注解在哪一個級別保留該注解信息毅整,可選的RetentionType參數(shù)包含
// 存在于Java源文件,注解被編譯器丟棄
SOURCE
// 存在于Java源文件绽左,以及經(jīng)編譯器后生成的Class字節(jié)碼文件悼嫉,但在運行時VM不再保留注解
CLASS
// 存在于源文件、編譯生成的Class字節(jié)碼文件妇菱,以及保留在運行時VM中承粤,因此可通過反射讀取注解
RUNTIME
**Target **
Target表示該注解可以用于什么地方暴区,可能使用的參數(shù)ElementType包括
// 注解類型,表示這個注解只能用于注解類型
// 比如Target辛臊,Rentention仙粱,Inherited,Documented這些元注解都是用于注解類型的
ANNOTATION_TYPE
// 構(gòu)造器聲明
CONSTRUCTOR
// 字段聲明(包括enum實例)
FIELD
// 局部變量聲明
LOCAL_VARIABLE
// 方法聲明
METHOD
// 包聲明
PACKAGE
// 參數(shù)聲明
PARAMETER
// 類彻舰,接口(包括注解類型)或者enum聲明
TYPE
Documented
當(dāng)前注解的元素會被javadoc工具進(jìn)行文檔化伐割,那么在查看Java API文檔時可查看該注解元素。
Inherited
允許子類繼承父類中的注解
注解元素
在注解中還會包含一些元素表示值刃唤,當(dāng)使用Class里面的方法分析處理注解的時候隔心,程序就可以訪問這些值。在注解中定義元素就像在普通接口中定義方法尚胞,但是注解可以使用default定義元素的默認(rèn)值硬霍。對于沒有元素的注解,我們可以把它作為標(biāo)記來使用笼裳。比如被@Test標(biāo)記的方法為測試方法
注解元素可用的類型包括如下幾個
基本數(shù)據(jù)類型
String
Class
enum
Annotation
以上類型的數(shù)組
對于注解里面的元素唯卖,必須有一個確定的值,不能夠使用null這種未定義的值作為默認(rèn)值躬柬,所以我們可以使用空對象這樣的概念來解決這個問題拜轨,比如定義空字符串作為字符串為null的情況。
解析注解
在很多ORM數(shù)據(jù)庫框架中都使用了注解來定義Bean類允青,直接使用Bean類生成數(shù)據(jù)庫表橄碾。比如ORMLite。
如果不對注解進(jìn)行解析的話颠锉,其實注解就沒什么意義了法牲,可以通過Annotation中提供的API來訪問注解。那么先來看一下Class提供給我們的用于解析注解的結(jié)構(gòu)方法
// 如果當(dāng)前元素包含指定的注解類型木柬,則返回該注解對象皆串,如果不存在則返回null
<A extends Annotation> getAnnotation(Class<A> annotationClass)
// 返回這個元素上的所有注解
Annotation[] getAnnotations()
// 返回直接定義在這個元素上的注解
Annotation[] getDeclaredAnnotations()
// 如果當(dāng)前這個元素包含指定的注解類型則返回true
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
在Android中也有通過注解實現(xiàn)的IOC,我們在編寫程序的時候眉枕,如果xml里面有很多很多的控件恶复,這樣就需要寫很多遍findViewById,不但寫起來很累速挑,而且很占空間谤牡,于是人們就想到了通過注解來減輕這樣的編寫“樣板”代碼的負(fù)擔(dān)。
ViewInject注解類
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ViewInject {
int id() default -1;
}
BaseActivity基類姥宝,使用模板方法
public abstract class BaseActivity extends Activity {
private Context mContext;
private void inject() {
Class activity = getClass();
//獲取Activity內(nèi)所有的字段
Field[] fields = activity.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
// 獲取指定的注解類型翅萤,如果返回null則跳過
ViewInject viewInject = field.getAnnotation(ViewInject.class);
if (viewInject != null) {
// 獲取指定的屬性
int value = viewInject.id();
try {
// 反射獲取findViewById方法
Method method = activity.getMethod("findViewById", int.class);
method.setAccessible(true);
// 調(diào)用該方法,因為findViewById要求是在Activity對象上的方法
Object object = method.invoke(mContext, value);
field.set(mContext, object);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
protected abstract int requestLayout();
protected abstract void bindView();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(requestLayout());
mContext = this;
inject();
bindView();
}
}
MainActivity實現(xiàn)類
public class MainActivity extends BaseActivity {
@ViewInject(id=R.id.text)
private TextView textView;
@Override
protected int requestLayout() {
return R.layout.layout_annotation;
}
@Override
protected void bindView() {
textView.setText("1131");
}
}
現(xiàn)在大致上那些retrofit腊满,dagger套么,butterknife使用的注解也是基于這個原理的罷培己,有空去好好研究一下這幾個開源框架的源碼,使用注解胚泌,我們應(yīng)該還需要有類加載省咨,泛型,反射等基礎(chǔ)知識玷室,才能夠把注解玩的飛起零蓉。