今日无事,应狒狒之邀,写一篇详细的关于注解的文章。
注解一般有如下几种作用
- 生成文档,这是大家最常见的也是Java最早提供的注解功能。比如看源码的时候方法注释上面的 @see @param @return 等等;
- 减少配置,可以进行运行时动态处理,得到注解信息,实现代替配置文件的功能;也可以通过插件进行编译时处理,以解决解析注解而导致的反射性能消耗;
- 减少重复工作,比如第三方框架ButterKnife等,通过注解@BindView减少对findViewById的调用;
- 限定作用域,例如在编译时进行格式检查,如@override 放在方法前,如果你这个同名方法并不是覆盖了超类方法,则编译时就能检查出,用注解替代枚举等等;安卓也提供了一些参数注解来限定资源参数的传入,后面我们会讲到;
注解的优点:方便,简洁,配置信息和 Java 代码放在一起,有助于增强程序的内聚性。
注解的缺点:分散到各个class文件中,所以很不方便维护,比如一些路由注解,打在activity上,尤其是支持多路径的时候,找个错能让你疯。
总体来说注解对于编码带来的好处要多于坏处的,尤其是在是恰当的使用的时候,完全可以减少或者避免他所带来的缺点的。
了解了上面这些之后我们就来看看怎么用好注解这个利器吧。
内容可能比较多,视情况分篇写。免得您看的头疼眼花从了解到放弃。
Android大部分用的Java,我们只谈Java注解和怎么自定义适合自己的注解
元注解
Java语言提供几种基本的元注解,了解并掌握它们之后我们才可以做到得心应手的自定义自己需要的注解。
Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。
- @Target
- @Retention
- @Documented
- @Inherited
@Target指定了注解对象使用范围,也就是限制了被@Target修饰的注解的可以放在什么地方上面,可以修饰:packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)
@Target取值(ElementType)有:
- CONSTRUCTOR:用于描述构造器
- FIELD:用于描述域
- LOCAL_VARIABLE:用于描述局部变量
- METHOD:用于描述方法
- PACKAGE:用于描述包
- PARAMETER:用于描述参数
- TYPE:用于描述类、接口(包括注解类型) 或enum声明
比如我们见到最多的系统方法注解@Override
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
就通过@Target指定了该注解只作用于method,也就是类方法。
@Retention指定了注解对象作用的生命周期,比如有些注解是在编码阶段进行合法化检查的,只需要在源码里保留就行。无需编译进dex文件中。再比如有些注解是需要运行时通过反射获取的,就需要一直保留着。
@Retention取值(RetentionPoicy)有:
- SOURCE:在源文件中有效
- CLASS:在class文件中有效
- RUNTIME:在运行时有效
我们上面的@Override例子里的@Retention(RetentionPolicy.SOURCE)就是表示@Retention(RetentionPolicy.SOURCE)注解只在源码阶段有效。
@Documented是一个标记注解,它代表着此注解会被javadoc等文档生成工具提取到文档内。在doc文档中的内容会因为此注解的信息内容不同而不同。@Documented没有属性成员。
@Inherited也是一个标记注解,它声明了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的注解类型被作用于一个class,则这个注解将被作用于该class的所有子类。
需要注意的是@Inherited注解只会被标注过的class的子类所继承。类并不会从它所实现的接口继承该注解,方法也不会从它所重载的方法继承该注解。
而且当@Inherited标注的注解的Retention属性值是RetentionPolicy.RUNTIME时,反射API会增强这种继承性。也就是说当我们使用java.lang.reflect去查询一个标注了被@Inherited修饰的注解所标注时,反射代码会去检查类和其父类,直到指定的注解类型被发现,或者到达类继承结构的顶层。
以上就是Java提供的元注解的介绍了,接下来我们看自定义注解
自定义注解
@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、类、String、enum)。可以通过default来声明参数的默认值。
使用@interface自定义注解时,会自动继承java.lang.annotation.Annotation接口,编译程序会自动完成其他细节。
定义注解时,不能继承其他的注解或接口。
自定义注解格式为
public @interface 注解名 {定义体}
注解的参数只支持以下数据类型
- 所有基本数据类型(int,float,boolean,byte,double,char,long,short)
- String类型
- Class类型
- enum类型
- Annotation类型
- 以上所有类型的数组
需要注意的点
- 注解的参数只能用public或默认(default)这两个访问修饰符
- 如果只有一个参数成员,最好把参数名称设为value后加小括号
- 注解元素必须有确定的值,要么在定义注解的默认值中指定,要么在使用注解时指定,非基本类型的注解元素的值不可为null。因此, 使用空字符串或0作为默认值是一种常用的做法。这个约束使得处理器很难表现一个元素的存在或缺失的状态,因为每个注解的声明中,所有元素都存在,并且都具有相应的值,为了绕开这个约束,我们只能定义一些特殊的值,例如空字符串或者负数,一次表示某个元素不存在(这段话抄的)
例:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface BindView {
int value() default -1;
}
以上就是自定义注解的要点了。
下一篇:Android提供的常用注解以及自定义限制作用注解
下下一篇:怎么定义以及代码解析运行时注解
有什么建议的可以留言喔
如果我的博客对您有帮助,请留言鼓励下或者点个赞吧!
我建了一个QQ群(群号:121606151),用于大家讨论交流Android技术问题,有兴趣的可以加下,大家一起进步。