最近在研究阿里开源的ARouter框架,碰到了较多注解的使用。结合ButterKnife开源框架的使用,越来越体会到熟练使用注解的重要性。故针对注解再次进行学习和总结,重要的是辅助代码来验证。
Annotation注解
Annontation是Java5开始引入的新特征,中文名称叫注解。它提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)进行关联。为程序的元素(类、方法、成员变量)加上更直观更明了的说明,这些说明信息是与程序的业务逻辑无关,并且供指定的工具或框架使用。Annontation像一种修饰符一样,应用于包、类型、构造方法、方法、成员变量、参数及本地变量的声明语句中。
Java注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用。包含在 java.lang.annotation 包中。
JDK内置注解
常用的标准注解有:
- @Override,主要用于在子类中覆盖父类中的方法
- @Deprecated,用来标志被弃用的代码,编译器会进行警告
-
@SuppressWarnings,用于忽略编译器的警告信息。
相信大家在日常工作中经常使用以上三种注解。
元注解
开发人员可以根据自己的需要自定义注解,这时需要用到@Retention, @Target, @Inherited, @Documented这几个元注解。
- @Rentention定义注解的保留级别。取值范围是RetentionPolicy类型,源码定义如下:
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
}
包括SOURCE:在代码编写阶段存在,编译时被丢弃。如@Override
CLASS:编译阶段注解保留在class文件中,VM运行时不需要保留。一般用来动态生成java代码,如ARouter,ButterKnife框架
RUNTIME:编译阶段注解保留在class文件中,VM运行时仍保留注解。一般与反射结合使用,如Retrofit。
- @Target定义注解可以用于什么对象。取值范围是ElementType类型,源码定义如下:
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
}
其中常用的类型有TYPE:类、接口(包括注解)、枚举
FIELD:成员变量
METHOD:成员方法
PARAMETER:方法参数
ANNOTATION_TYPE:注解的注解
@Documented标志注解将包含在Javadoc中
@Inherited允许子类继承父类中的注解
关于每种元注解的具体定义,可以在Android Studio中方便的查看源码文件。reading the fuck code!
自定义注解
开发人员可以使用上面的元注解来定义自己的注解。示例如下:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RuntimeAnno {
String value();
boolean isAlive() default false;
}
使用@interface定义注解,类名即为注解名。自定义注解中只能定义方法,所有的方法均没有参数,也没有修饰符(默认是public&abstract修饰符),方法的返回值是基本类型、String、Classs、Annotation、Enum或者对应的一位数组。方法可以通过default设置默认值。
注解使用时,如果只有一个方法,则可直接“注解名(值)”来使用;如果有多个方法,则需要依次赋值“注解名(方法名=值,方法名=值...)”。如果方法使用default赋值,则不必在使用时显示赋值。
@RuntimeAnno(value="11111")
@RuntimeAnno("22222")
@RuntimeAnno(value="33333", isAlive = true)
典型的注解定义如下图:
其中,ButterKnife的OnClick注解使用了自定义注解ListenerClass,具体定义可参考ButterKnife源码。
运行时(RUNTIME)注解的解析
以Method为例,参考Method的源码,可以看到有如下几个方法:
/**
* {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
* @since 1.5
*/
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
return super.getAnnotation(annotationClass);
}
/**
* {@inheritDoc}
* @since 1.5
*/
public Annotation[] getDeclaredAnnotations() {
return super.getDeclaredAnnotations();
}
/**
* {@inheritDoc}
* @since 1.5
*/
@Override
public Annotation[][] getParameterAnnotations() {
// Android-changed: This is handled by Executable.
return super.getParameterAnnotationsInternal();
}
其中getAnnotation(AnnotationName.class) 可获取该 Target 某个 Annotation 的信息;因为一个 Target 可以被多个 Annotation 修饰.
getAnnotations() 则可获取该 Target 所有 Annotation. getParameterAnnotations可以获取方法的所有参数的注解,返回值是一个二维数组。
其他Target,如Filed,Class,获取Annotation的方法可参考java源码。
高版本的jdk还有isAnnotationPresent(AnnotationName.class) 方法,来判断Target 是否被某个 Annotation 修饰。
编译时(CLASS)注解解析
编译器解析编译时注解,需要做的是1、自定义类继承AbstractProcessor;2、重写process函数。具体使用方法,可参考网上的其他文档。
ButterKnife就是使用编译时注解,生成JAVA文件。
注解的作用
1、JDK标准注解,可用来生成JavaDoc,如@param @return;可用来进行代码检查,如@Override,@Deprecated。
2、目前较多框架使用注解,来自动生成代码或替代配置文件的功能。注解可以使编码更简洁,学习注解可以理解使用开源框架,甚至自定义框架来解决问题。
3、注解和反射配合使用,可以使用简洁的代码实现复杂的功能,笔者目前的网络库入参就采用了注解,动态代理和反射技术。可以参考反射注解与动态代理综合使用,该文档写的简单易懂。