前言:为什么要写这篇文章?主要是为了总结一下最近学习内容,记录方便以后查阅。以前有一次面试有人问到看过 ButterKnife 实现的原理是什么?答:注解,反射。然而真的是这样吗?。也在网络上看到很多关于这方面的资料,稂莠不齐,所以趁这阶段有时间特地把 Annotation 搞搞清楚。文中会出现引用一些文章的部分,文末已注明出处,表示感谢。
CkID59daIh.png代码地址:android-annotation-tutorial
一、为什么需要了解注解
- 为了看懂如 Retrofit,Arouter, dagger2,ButterKnife 等开源库,不得不先看懂注解
- Java虚拟机可以完成这些标示对应的功能,通过注解我们可以完全代替配置文件的编写
- 注解将一些本来重复性的工作,变成程序自动完成,简化和自动化该过程
- 为了更好地提升开发效率和代码质量
二、什么是注解
An annotation is a form of metadata, that can be added to Java source code. Classes, methods, variables, parameters and packages may be annotated. Annotations have no direct effect on the operation of the code they annotate.
能够添加到 Java 源代码的语法元数据。类、方法、变量、参数、包都可以被注解,可用来将信息元数据与程序元素进行关联。Annotation 中文常译为“注解”。
三、分类
按照来源划分
类型 | 说明 |
---|---|
来自JDK的注解 | java.lang.annotation.* 等 |
来自Android API的注解 | android.annotation.* ,如TargetApi、SuppressLint等 |
来自第三方的注解 | 如 support-annotations |
自定义注解 | N/A |
JDK 内置的几个常用注解:
-
@Override——当我们想要复写父类中的方法时,我们需要使用该注解去告知编译器我们想要复写这个方法。这样一来当父类中的方法移除或者发生更改时编译器将提示错误信息。
-
@Deprecated——当我们希望编译器知道某一方法不建议使用时,我们应该使用这个注解。Java在javadoc 中推荐使用该注解,我们应该提供为什么该方法不推荐使用以及替代的方法。此注解是对不应该,或者将要淘汰的方法进行标识,当编程人员使用时就会给予提示。这个功能在一些基础jar包,规范,框架等的中我们经常可以看到,还保留这些方法是为了兼容前边的一些版本,有一过度的阶段。看下边的这种效果应该都见过:
-
@SuppressWarnings——这个仅仅是告诉编译器忽略特定的警告信息,例如在泛型中使用原生数据类型。它的保留策略是SOURCE(译者注:在源文件中有效)并且被编译器丢弃。此注解表示去除一些警告,但是里边需要我们制定参数
-
@Documented—— 指明拥有这个注解的元素可以被javadoc此类的工具文档化。这种类型应该用于注解那些影响客户使用带注释的元素声明的类型。如果一种声明使用Documented进行注解,这种类型的注解被作为被标注的程序成员的公共API。
-
@Target——指明该类型的注解可以注解的程序元素的范围。该元注解的取值可以为TYPE,METHOD,CONSTRUCTOR,FIELD等。如果Target元注解没有出现,那么定义的注解可以应用于程序的任何元素。
-
@Inherited——指明该注解类型被自动继承。如果用户在当前类中查询这个元注解类型并且当前类的声明中不包含这个元注解类型,那么也将自动查询当前类的父类是否存在Inherited元注解,这个动作将被重复执行知道这个标注类型被找到,或者是查询到顶层的父类。允许子类继承父类中的注解
-
@Retention——指明了该Annotation被保留的时间长短。RetentionPolicy取值为SOURCE,CLASS,RUNTIME。
来自 JDK 的注解着重关注一下元注解,也就是第三种分类标准;
在安卓源码中有很多如下代码段,
static {
if (VERSION.SDK_INT < VERSION_CODES.KITKAT) {
ROOT_REATTACHED_LISTENER = null;
} else {
ROOT_REATTACHED_LISTENER = new OnAttachStateChangeListener() {
@TargetApi(VERSION_CODES.KITKAT)
@Override
public void onViewAttachedToWindow(View v) {
// execute the pending bindings.
final ViewDataBinding binding = getBinding(v);
binding.mRebindRunnable.run();
v.removeOnAttachStateChangeListener(this);
}
@Override
public void onViewDetachedFromWindow(View v) {
}
};
}
}
@TargetApi(VERSION_CODES.KITKAT)
如果只加这个注解,表明这段代码只能在19及以上的系统上运行,如果你非要在19以下的系统上运行,那该警告的已经警告了,你只是忽略了警告,但运行时该错还是错;
关于 Android 注解具体介绍请看下一篇文章
按照运行机制划分
类型 | 说明 |
---|---|
源码注解 RetentionPolicy.SOURCE | 标记,用于告诉编译器一些信息 |
编译时注解 RetentionPolicy.CLASS | 编译时动态处理,如动态生成代码 |
运行时注解 RetentionPolicy.RUNTIME | 运行时动态处理,如得到注解信息 |
元注解
元注解的作用就是负责注解其他注解。java 1.5定义了4个meta-annotation类型,
在1.8中又添加了两个,用来提供对Annotation 类型做说明。该注解位于包
java.lang.annotation 下:
元注解 | 详情 |
---|---|
@Target | since 1.5 用于描述注解的使用范围 |
@Retention | since 1.5 定义了该Annotation被保留的时间长短 |
@Document | since 1.5 用于描述其它类型的annotation应该被作为被标注的程序成员的公共API |
@Inherited | since 1.5 阐述了某个被标注的类型是被继承的 |
@Repeatable | since 1.8 N/A |
@Native | since 1.8 N/A |
四、最后
优点
- 集中管理对象和对象之间的组合关系,易于阅读
- 编译期间容易发现错误的出处
缺点
- 运行中的错误很难定位,调试难度较大
- 管理分散,基本每个类上都有
何时选择
如果客户需求进行发生变化,那么采用配置文件的方式会好一些 , 有利于扩展。
如果客户需求不会频繁发生变化, 那么使用注解非常好,开发效率快.
最后提醒下:
要用好注解,必须熟悉java 的反射机制,注解的解析完全依赖于反射 , 不要滥用注解。平常我们编程过程很少接触和使用注解,只有做设计,且不想让设计有过多的配置时。
这篇文章中我们并不设计怎么使用注解,主要是能对它有个总体印象 , 后面三篇文章会按照运行机制来进行举例。
参考链接:
网友评论