前言
在Android开发作业中接触到了很多开源框架使用了Java Annotation机制,我接触到的就有GreenRobot、Dagger2、AndFix等项目。
那么 Annotation机制到底是如何发挥作用的?下面将介绍Annotation的常见类型及基本语法。
从@Override认识注解
相信大部分同学对@Override一点都不陌生,在子类覆盖超类的方法时,Eclipse等IDE会在方法上自动生成这个注解。
那么来看一下这个注解的语法形式:
package java.lang;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
其中@interface 定义了Override是一个Annotation类型,或者叫元数据(meta-data)。
@Target和@Retetion是对Override的注解,称之为元注解(元数据的注解)。
@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也是一个注解类型,在其内部定义了方法ElementType[] value();
细心观察就会发现这个方法的返回值就是@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
}
这个枚举其实就是定义了注解的适用范围,在Override注解中,@Target的属性是ElementType.METHOD,所以Override这个注解只能用于注解方法。
而Target注解本身也有一个@Target元注解,这个@Target元注解属性是ElementType.ANNOTATION_TYPE,也就是说Target注解只能用作元数据(注解)的注解,所以叫它元注解。
@Retention
@Target声明了Override注解只能使用代码中的方法定义,@Retention注解则定义了注解的保留范围,如:在源代码、CLASS文件或运行时保留。
超出@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注解用于检测子类有无实现超类或接口的抽象方法,所以只在编译阶段检测语法是否正确就足够了。
@Documented
@Documented也属于元注解:
package java.lang.annotation;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}
如果一个注解定义了@Ducumented,在javadoc API文档时,被这个注解标记的元素在文档上也会出现该注解,例如:
//自定义一个注解
@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)上出现@Subscribe。
@Inherited
该元注解比较特殊,只对@Target为ElementType.TYPE(类、接口、枚举)有效,并且只支持类元素。
使用了@Inherited的注解修饰在一个class上,可以保证继承它的子类也拥有同样的注解。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}
小结
通过常见的@Override我们认识了一个注解定义的语法,并且认识了四个基本元注解@Target、@Retention、@Documented、@Inherited以及它们的功能。
其中@Target用来指定注解可以修饰的源代码中的元素(构造器、方法、字段、包、参数、局部变量、类或接口),@Retention指定注解保留的范围(源代码、class文件、运行时),@Documented指定注解是否出现在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或以上类型的数组。
default关键字可以定义该属性的默认值,如果没有指定默认值在使用注解时必须显示指定属性值。
特别说明的是注解不支持像extends语法这样的继承。
自定义注解以及反射使用注解的例子
本例将仿照Eventbus使用注解的语法实现一个超简易版的Mybus。
//定义运行线程枚举
public enum ThreadMode {
Posting,Main;
}
//定义注解用于反射识别观察者方法
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Subscribe {
ThreadMode threadMode() default ThreadMode.Main;
}
//定义一个MyBus类用于注册、注销观察者或者发出通知给观察者,在发出通知时会遍历观察者集合,找出观察者中被@Subscribe 注解的方法,判断通知事件的类型与@Subscribe 注解方法的参数类型是否匹配,然后根据ThreadMode交给观察者在主线程或子线程处理。
public class MyBus {
LinkedList
测试结果:
tid 1 run A.a() ,event name is eventA
tid 10 run B.b(),event name is eventB
可以看到发出不同的事件A和B,最后根据观察者A和B中方法的注解找到方法处理,由注解中的ThreadMode属性指定在哪个线程执行处理方法。