前言
在Android開發(fā)作業(yè)中接觸到了很多開源框架使用了Java Annotation機(jī)制,我接觸到的就有GreenRobot、Dagger2、AndFix等項目。
那么 Annotation機(jī)制到底是如何發(fā)揮作用的?下面將介紹Annotation的常見類型及基本語法。
從@Override認(rèn)識注解
相信大部分同學(xué)對@Override一點(diǎn)都不陌生,在子類覆蓋超類的方法時,Eclipse等IDE會在方法上自動生成這個注解。
那么來看一下這個注解的語法形式:
package java.lang;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
其中@interface 定義了Override是一個Annotation類型,或者叫元數(shù)據(jù)(meta-data)。
@Target和@Retetion是對Override的注解,稱之為元注解(元數(shù)據(jù)的注解)。
@Target
再來看下@Target的定義:
package java.lang.annotation;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation type
* can be applied to.
* @return an array of the kinds of elements an annotation type
* can be applied to
*/
ElementType[] value();
}
首先Target也是一個注解類型,在其內(nèi)部定義了方法ElementType[] value();
細(xì)心觀察就會發(fā)現(xiàn)這個方法的返回值就是@Target(ElementType.METHOD)中的ElementType.METHOD,也就是注解的屬性,是一個ElementType枚舉。
再來看ElementType的定義:
package java.lang.annotation;
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE,
/** Field declaration (includes enum constants) */
FIELD,
/** Method declaration */
METHOD,
/** Formal parameter declaration */
PARAMETER,
/** Constructor declaration */
CONSTRUCTOR,
/** Local variable declaration */
LOCAL_VARIABLE,
/** Annotation type declaration */
ANNOTATION_TYPE,
/** Package declaration */
PACKAGE,
/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER,
/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE
}
這個枚舉其實(shí)就是定義了注解的適用范圍,在Override注解中,@Target的屬性是ElementType.METHOD,所以O(shè)verride這個注解只能用于注解方法。
而Target注解本身也有一個@Target元注解,這個@Target元注解屬性是ElementType.ANNOTATION_TYPE,也就是說Target注解只能用作元數(shù)據(jù)(注解)的注解,所以叫它元注解。
@Retention
@Target聲明了Override注解只能使用代碼中的方法定義,@Retention注解則定義了注解的保留范圍,如:在源代碼、CLASS文件或運(yùn)行時保留。
超出@Retention定義的屬性,注解將被丟棄。
package java.lang.annotation;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
*/
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
*/
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}
如果像Override注解中的@Retention定義RetentionPolicy .SOURCE屬性,那么生成CLASS文件時不會在方法上見到@Override。由于Override注解用于檢測子類有無實(shí)現(xiàn)超類或接口的抽象方法,所以只在編譯階段檢測語法是否正確就足夠了。
@Documented
@Documented也屬于元注解:
package java.lang.annotation;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}
如果一個注解定義了@Ducumented,在javadoc API文檔時,被這個注解標(biāo)記的元素在文檔上也會出現(xiàn)該注解,例如:
//自定義一個注解
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Subscribe {
ThreadMode threadMode() default ThreadMode.Main;
}
//使用自定義的注解
public class A {
@Subscribe
public void a(EventA a){
System.out.println("tid "+Thread.currentThread().getId()+" run A.a() ,event name is "+a.name);
}
}
在javadoc生成的文檔中可見:
@Subscribe
public void a(EventA a)
而不在Subsribe注解上添加@Documented則不會在方法a(EventA a)上出現(xiàn)@Subscribe。
@Inherited
該元注解比較特殊,只對@Target為ElementType.TYPE(類、接口、枚舉)有效,并且只支持類元素。
使用了@Inherited的注解修飾在一個class上,可以保證繼承它的子類也擁有同樣的注解。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}
小結(jié)
通過常見的@Override我們認(rèn)識了一個注解定義的語法,并且認(rèn)識了四個基本元注解@Target、@Retention、@Documented、@Inherited以及它們的功能。
其中@Target用來指定注解可以修飾的源代碼中的元素(構(gòu)造器、方法、字段、包、參數(shù)、局部變量、類或接口),@Retention指定注解保留的范圍(源代碼、class文件、運(yùn)行時),@Documented指定注解是否出現(xiàn)在javadoc生成的API文檔中的具體的元素上,@Inherited指定注解是否可以被子類繼承。
自定義注解
使用@interface可以定義一個注解,形如:
@Inherited
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Subscribe {
ThreadMode threadMode() default ThreadMode.Main;
}
在本例中threadMode()返回的是注解的一個屬性,其返回值可以是所有基本類型、String、Class、enum、Annotation或以上類型的數(shù)組。
default關(guān)鍵字可以定義該屬性的默認(rèn)值,如果沒有指定默認(rèn)值在使用注解時必須顯示指定屬性值。
特別說明的是注解不支持像extends語法這樣的繼承。
自定義注解以及反射使用注解的例子
本例將仿照Eventbus使用注解的語法實(shí)現(xiàn)一個超簡易版的Mybus。
//定義運(yùn)行線程枚舉
public enum ThreadMode {
Posting,Main;
}
//定義注解用于反射識別觀察者方法
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Subscribe {
ThreadMode threadMode() default ThreadMode.Main;
}
//定義一個MyBus類用于注冊、注銷觀察者或者發(fā)出通知給觀察者,在發(fā)出通知時會遍歷觀察者集合,找出觀察者中被@Subscribe 注解的方法,判斷通知事件的類型與@Subscribe 注解方法的參數(shù)類型是否匹配,然后根據(jù)ThreadMode交給觀察者在主線程或子線程處理。
public class MyBus {
LinkedList<Object> mObjs = new LinkedList<Object>();
public void register(Object o) {
mObjs.add(o);
}
public void unregister(Object o) {
mObjs.remove(o);
}
public void post(final Object event) {
// System.out.println("post參數(shù)類型:"+event.getClass().getCanonicalName());
Iterator<Object> iterator = mObjs.iterator();
while (iterator.hasNext()) {
final Object obj = iterator.next();
Class<? extends Object> cl = obj.getClass();
// System.out.println("遍歷類 "+cl.getCanonicalName());
for (final Method method : cl.getDeclaredMethods()) {
Subscribe subscribe = method.getAnnotation(Subscribe.class);
if (subscribe != null) {
// System.out.println("找到注解的方法 "+method.getName());
if (method.getParameterCount() == 1) {
Class pmClass = (method.getParameterTypes())[0];
// System.out.println(method.getName()+"的參數(shù)類型:"+pmClass.getCanonicalName());
if (pmClass.equals(event.getClass())) {
// 判斷參數(shù)的類型是post參數(shù)類型的超類或接口或相等
// System.out.println(method.getName()+" 是合法的");
try {
if (subscribe.threadMode() == ThreadMode.Main) {
method.invoke(obj, event);
}else{
new Thread(){
public void run() {
try {
method.invoke(obj, event);
} catch (IllegalAccessException
| IllegalArgumentException
| InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
};
}.start();
}
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
}
}
}
//事件A
public class EventA {
public String name;
}
//事件B
public class EventB {
public String name;
}
//觀察者A
public class A {
@Subscribe
public void a(EventA a){
System.out.println("tid "+Thread.currentThread().getId()+" run A.a() ,event name is "+a.name);
}
}
//觀察者B
public class B {
@Subscribe(threadMode=ThreadMode.Posting)
public void b(EventB e){
System.out.println("tid "+Thread.currentThread().getId()+" run B.b(),event name is "+e.name);
}
}
//測試
public class Main {
public static void main(String[] args) {
A a= new A();
B b=new B();
MyBus bus = new MyBus();
bus.register(a);
bus.register(b);
EventA eventA = new EventA();
eventA.name="eventA";
EventB eventB = new EventB();
eventB.name = "eventB";
bus.post(eventA);
bus.post(eventB);
}
}
測試結(jié)果:
tid 1 run A.a() ,event name is eventA
tid 10 run B.b(),event name is eventB
可以看到發(fā)出不同的事件A和B,最后根據(jù)觀察者A和B中方法的注解找到方法處理,由注解中的ThreadMode屬性指定在哪個線程執(zhí)行處理方法。