元數(shù)據(jù)
元數(shù)據(jù)是指用來描述數(shù)據(jù)的數(shù)據(jù)(“data about data”),簡單的說,就是描述代碼間關(guān)系拴还,代碼本身,資源數(shù)據(jù)等的數(shù)據(jù)鸦难。一般是結(jié)構(gòu)化數(shù)據(jù)(如存儲在數(shù)據(jù)庫里的數(shù)據(jù)根吁,規(guī)定了字段的長度、類型等)合蔽。元數(shù)據(jù)是指從信息資源中抽取出來的用于說明其特征击敌、內(nèi)容的結(jié)構(gòu)化的數(shù)據(jù)。
比如拴事,關(guān)于一本書(信息資源)沃斤,我們在圖書館系統(tǒng)中檢索可以得到題名,版本、出版數(shù)據(jù)刃宵、相關(guān)說明,包括檢索點等衡瓶,這些就是元數(shù)據(jù),用于描述資源的牲证。
Java注解
JDK5.0引入了Annotation的概念來描述元數(shù)據(jù)哮针,在java中,元數(shù)據(jù)是以標簽的形式存在,元數(shù)據(jù)并不會影響程序代碼的編譯和執(zhí)行坦袍。JDK5.0出來后诚撵,java語言中有四種類型,class(類)键闺、enum(枚舉)寿烟、interface(接口)和@interface(注解),他們在java中處于同一級別辛燥。元數(shù)據(jù)在java中筛武,既是由注解來表示的。注解可以在編譯挎塌、類加載和運行時被讀取徘六。
Java的注解本質(zhì)上是一個接口,繼承了接口Annotation的接口榴都,這里的繼承用的是extends待锈。
注解既然和接口類似,就會包含成員:
- final靜態(tài)屬性嘴高,必須初始化
- 公共抽象方法竿音,可設(shè)置默認值
@interface隱含繼承Annotation,但是自定義注解不能直接繼承Annotation。直接繼承的是接口拴驮,非注解春瞬。
注解的作用
- 編寫文檔(通過代碼里面標識的元數(shù)據(jù)編寫,例如@param,@return)
- 代碼分析(可以替代配置文件)
- 編譯檢查(通常配合lint使用套啤,如@override 放在方法前宽气,如果你這個方法并不是覆蓋了超類方法,則編譯時就能檢查出。)
Java中自帶的注解
(基本)
- @Override(限定重寫父類的方法)
- @SuppressWarning(用于抑制編譯器產(chǎn)生警告信息)
- @Deprecated(表示"已過時/不建議使用/在后期的API中可能被刪除")
- @SafeVarargs(java7新增萄涯,和堆污染有關(guān)绪氛,具體不是很了解)
(用于標識注解的注解) - @Retention(決定注解存活和讀取時間,它包含一個RetationPolicy的value成員變量涝影,用于指定它所修飾的注解的讀取和存活時間)
一般的值有:- Retationpolicy.CLASS:在類加載的時候解讀钞楼,執(zhí)行的時候,jvm就會拋棄掉。
- Retationpolicy.ROURCE:存在于源碼中袄琳,在編譯的時候解讀询件,之后就拋棄,不進入類加載和運行環(huán)節(jié)唆樊。
- Retationnpolicy.RUNTIME:運行時解讀宛琅,可以在運行時通過反射獲取注解,執(zhí)行操作逗旁。
- @Target (指定修飾的元素嘿辟,包含一個value值)
value可選:- ElementType.ANNOTATION_TYPE: 指定該Annotation只能修飾Annotation。
- ElementType.CONSTRUCTOR: 指定只能修飾構(gòu)造器片效。
- ElementType.FIELD: 指定只能成員變量红伦。
- ElementType.LOCAL_VARIABLE: 指定只能修飾局部變量。
- ElementType.METHOD: 指定只能修飾方法淀衣。
- ElementType.PACKAGE: 指定只能修飾包定義昙读。
- ElementType.PARAMETER: 指定只能修飾參數(shù)。
- ElementType.TYPE: 指定可以修飾類膨桥,接口蛮浑,枚舉定義。
- @Document(可被javadoc提取成文檔)
- @Inherited 被他修飾的Annotation具有繼承性
自定義注解
最簡單的例子只嚣,Android中代替findviewById()對組件進行初始化沮稚。只需要在成員變量組件前加上注解就行,能有效的減少代碼量册舞,可讀性更強蕴掏。
首先定義注解,運行時讀取调鲸,作用于成員變量
寫法和接口雷同
用法是@FindView(R.id.XXX)這里傳入的Id就是這個value的返回值盛杰,可以在獲取到這個注解實例后調(diào)用這個value() 方法獲取值
可以不止一個方法,多一個方法线得,就可以多傳一個參數(shù)饶唤,如果有默認值,也可以不傳
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface FindView{
/**
* id 注解贯钩,這里沒有默認值,不過可以自由添加,
* 例如:public String tableName() default "className";
* @return
*/
int value();
}
從上面注解的定義來角雷,類似于接口祸穷,所以注解本身不具備解析和其他操作的功能,這里我們想要的是利用這個注解攜帶的value勺三,初始化這個注解所表示的成員變量雷滚,所以我們需要自己寫一個工具類,通過傳入的activity獲取注解吗坚,并利用反射初始化成員變量祈远。以下是詳細代碼:
public class ViewUtil {
public static void viewInject(Activity activity){
viewInjects(activity);
}
private static void viewInjects(Activity activity){
Class<? extends Activity> clazz = activity.getClass();
Field[] fields = clazz.getDeclaredFields();
for(Field field:fields){
FindView viewFind = field.getAnnotation(FindView.class);
if(viewFind!=null){
int viewId = viewFind.value();
View view = activity.findViewById(viewId);
try {
field.setAccessible(true);
field.set(activity, view);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
最后是具體的使用方法
public class MainActivity extends AppCompatActivity {
@FindView(R.id.btn)
Button btn;
@FindView(R.id.btn1)
Button btn1;
@FindView(R.id.btn2)
Button btn2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ViewUtil.viewInject(this);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
btn.setText("Test2");
}
});
}
}
以上是@Retention(RetentionPolicy.RUNTIME)運行時解讀的具體寫法。
優(yōu)點是可讀性強商源,寫起來簡單车份。
缺點便是常駐內(nèi)存,并利用反射進行初始化牡彻,耗費內(nèi)存
如何解決這個問題呢扫沼?其實我們可以利用apt,使用編譯時解讀或者類加載時解讀庄吼,后面的文章我會重點講到缎除,這里就不多啰嗦了。
So easy!