Java提供了Annotations来对代码提供一些注解,以解释、约束代码,也可以称为编程的元数据。到底什么是Annotation呢,举几个大家熟悉的例子如@Override(表示重载)、@Deprecated(表示下述方法不推荐使用,通常标注为此会提供推荐方法)等,Butter Knife也是Android开发常用的库,GitHub上Star达到了6200+,它在简化代码时主要用到的也是注解技巧。
@Override
public void onCreate(Bundle savedInstanceState);Butter Knife
@Bind(R.id.user) EditText username;
之所以叫Android Annotations,是因为Android的注解比Java多提供了一个库“android.support:support-annotations”,可以说是Java注解的扩展。接下来本文经过查证参考,总结整理了其基本的概念,因为翻译水平有限,涉及定义的地方将对文档原文有所引用。望各位批评指正~
先摘录Wiki和Oracle的原文定义如下:
Wiki
An annotation, in the Java computer programming language, is a form of syntactic metadata that can be added to Java source code. Classes, methods, variables, parameters and packages may be annotated. Unlike Javadoc tags, Java annotations can be reflective in that they can be embedded in class files generated by the compiler and may be retained by the Java VM to be made retrievable at run-time. It is possible to create meta-annotations out of the existing ones in Java.
Oracle
Annotations, a form of metadata, provide data about a program that is not part of the program itself. Annotations have no direct effect on the operation of the code they annotate.
Annotation,中文一般译为“注解”,表示为程序语言的元数据格式,可以添加如Java的源代码中,类、方法、变量、参数、包都可以被注解。不同于Javadoc的标签,Java的注解如果设置为留存到VM可识别的运行时态,那么它是可以通过反射获取出来的。注解本身对他们所注解的代码没有直接的影响。
很多资料对Java Annotation进行了分类,大部分是分为:Java内置、元注解、自定注解。这里因为考虑到Android库提供的注解,我将其总结为以下几种类型:
Built-in Java Annotations(java.lang)
Java内置注解;定义在java.lang中,最常用的三个是@Deprecated、@Overrride、@SuppressWarnings。后续补充了两个@SafeVarargs和@FunctionsalInterface,后者是在Java 8添加的。具体的介绍如下:
Meta-annotations(java.lang.annotation)
元注解;定义在java.lang.annotation中,定义为用来描述注解的注解(Annotations that apply to other annotations are called meta-annotations. )。常见的四个类型是@Documented、@Inherited、@Retention、@Target。在Java 8新增了两个:@Repeatable和@Native。具体描述如下:
@Documented
通过这种方式定义,并使用了Foo注解,使用Android Studio的Tools->Generate JavaDoc 生成的结果中,对Foo的解释:
将上述注解定义中@Documented去掉,重新生成JavaDoc,结果是:
@Documented就是控制该注解是否写进JavaDoc文档中。
使用@Documented并不是把Annotation转化成JavaDoc注释语言形如:
而是直接写入文档。
@Inherited
指示注解类型被自动继承。如果在解析注解时发现了该字段,并且在该类中没有该类型的注解,则对其父类进行查询。举个例子,如果一个类中,没有A注解,但是其父类是有A注解的,并且A注解是被@Inherited注解的(不妨认为保留时态是Runtime),那么使用反射获取子类的A注解时,因为获取不到,所以会去其父类查询到A注解。使用了@Inherited注解的类,这个注解是可以被用于其子类。
@Retention
Indicates how long annotations with the annotated type are to be retained.
指示该注解的保留时间,即表明注解的生存周期。参数分为以下几类:
@Target
ElementType 常量在 Target 注解中至多只能出现一次,如下是非法的:@Target({ElementType.FIELD, ElementType.METHOD, ElementType.FIELD})
@Repetable
public @interface Authority { String role();} public @interface Authorities { Authority[] value();} public class RepeatAnnotationUseOldVersion { @Authorities({@Authority(role="Admin"),@Authority(role="Manager")}) public void doSomeThing(){ }}
缺点是可读性很差,正违背了注解本身的含义。使用@Repetable后,可以直接对其重复使用,如下:
@Repeatable(Authorities.class)public @interface Authority { String role();} public @interface Authorities { Authority[] value();} public class RepeatAnnotationUseNewVersion { @Authority(role="Admin") @Authority(role="Manager") public void doSomeThing(){ }}
@Native
“Indicates that a field defining a constant value may be referenced from native code.”
用来标记Native的属性。
这是一个比较特殊的注解,在Oracle Tutorial文档中,并没有把它描述成一个meta-annotations( https://docs.oracle.com/javase/tutorial/java/annotations/predefined.html),而在Oracle Java 8文档中,将其放在Annotation Types Summary(之前版本元注解都放在这个类型中解释)的表述中(https://docs.oracle.com/javase/8/docs/api/java/lang/annotation/package-summary.html)。
Android Support Annotations(android.support:support-annotations)
Android扩展注解;android.support:support-annotations提供的一些适用于Android的注解类型,目前更新到22版本。
这个Annotation只是一个指示作用,告诉其他开发者该函数为什么有这么大的可见程度(为了测试单元或者其他类对其测试使用)。
因此经常用来修饰public和protected,用其修饰private不会报错,但是意义很小。
它不能改变任何权限。
Guava has a @VisibleForTesting annotation, but it's only for documentation purpose.
A simple annotation that tells you why a particular property access restriction has been relaxed.A common trick to use in testing is to relax access restrictions to default for a particular property, so that you can use it in a unit test, which resides in the same package (though in different catalog). Whether you thing it's good or bad, remember to give a hint about that to the developer.
该注解用来说明,为什么该变量或者函数私有访问权限被释放成“公有”或者“package可见”。
通常来说,一个函数默认
这样定义,则是在package中可访问,而其他package的类无法访问,然而
单元测试形如
对此是有访问权限的。所以加上 @VisibleForTesting是说明,为什么你定义其他类似函数需要用private,这里这个test123函数需要释放私有访问权限呢?哦,原来是需要对测试单元可见(private函数 测试类是访问不了的)。
类似的,该注解经常和public一起使用,告诉大家为什么这个函数我现在设计成public的,是为了给其他测试的类使用的。总的来说@VisibleForTesting就是一个标记(Marker)。
以上都是搜到的解释,所以我写Demo也进行过大量验证,@ VisibleForTesting并不能改变权限,在单元测试以及其他package中,访问权限加不加该注解没有任何改变。
@VisibleForTesting没有那么强大,它只是一个很基本的注解。
This annotation is better than nothing in such a case because it at least makes it clear to others using the construct that there is a reason for its otherwise surprisingly relaxed visibility.
引自http://marxsoftware.blogspot.com/2011/11/two-generally-useful-guava-annotations.html
Marker Annotation
标记式注解;不指定任何元素,也不需要默认值,主要作用是对一些声明定义进行标记。
User-defined Annotations
自定义注解;
使用方法很简单,如下:
值得注意的是,何时可以使用注解,对其有人已经进行过整理,引用如下:
解析主要是对注解进行分析提取,获取其具体内容的过程,根据其生存周期不同,可以分为两种解析方式:运行时解析和编译时解析。
运行时解析
上述自定义注解解析可以在运行时进行。
编译时解析
关于编译时解析,主要问题在于自己如何实现apt(Annotation Processor Tool)的processor,编译环境如何使用它。
1、自定义Annotation;
2、通过apt提供的接口,自定义Annotation processor类,实现主要的处理方法;
3、注册自定的Processor,以便apt识别;
4、在使用的module内指定使用apt来编译该processor。
Demo具体实现方法如下:
新建三个module:
分别是:api、compiler、app。
Api和compiler定义为普通Java项目即可,尤其是compiler,最好是Java项目,因为Android项目不提供apt接口的支持,需要添加插件。
Api:
Java项目:apply plugin: 'java',在此处实现自定义Annotation
Compiler:
Java项目:apply plugin: 'java',在此处实现对Annotation的解析
此处需要让编译器知道你自定义了这个Processor,所以需要在此实现services的注册:
方法一:resources资源文件夹下新建
META-INF/services/javax.annotation.processing.Processor,并将自定义的processor名称加入。形如:
内容为
方法二:使用我用红色框标出来的部分是google提供的库(@AutoService):
compile 'com.google.auto.service:auto-service:1.0-rc2'
可以通过注解的方式自动添加services注册。
App:
Android项目:
apply plugin: 'com.android.application'
apply plugin: 'com.neenbedankt.android-apt'
该module是指具体功能的实现部分,在此只需要使用注解即可。
三个模块的gradule分别为:
Api:
Compiler:
解析模块依赖api模块。
App:
这里我们可以看到该模块不仅依赖api模块,还需要告诉编译器编译时需要apt对compiler进行解析(apt procject(‘:compiler’))。
有些人建议另开项目,实现自定义Annotation和apt的Processor,并封装成jar包放在Android项目中,原理是一样的。
Build之后,结果如下:这里在解析自定义apt时我只是让其打印了使用Annotation的类和方法名称。
总结整理如下:
http://tutorials.jenkov.com/java/annotations.html
https://docs.oracle.com/javase/tutorial/java/annotations/index.html
http://www.trinea.cn/android/java-annotation-android-open-source-analysis/
http://www.java2s.com/Tutorials/Java/Java_Object_Oriented_Design/0740__Java_Annotation_Type.htm
https://en.wikipedia.org/wiki/Java_annotation#cite_note-3
http://tools.android.com/tech-docs/support-annotations
http://www.flysnow.org/2015/08/13/android-tech-docs-support-annotations.html
http://my.oschina.net/ososchina/blog/345288
http://www.cnblogs.com/guangshan/p/4886029.html
http://www.2cto.com/kf/201502/376988.html
https://github.com/JakeWharton/butterknife
http://brianattwell.com/android-annotation-processing-pojo-string-generator/ http://hannesdorfmann.com/annotation-processing/annotationprocessing101/ http://www.cnblogs.com/avenwu/p/4173899.html https://docs.oracle.com/javase/7/docs/technotes/guides/apt/GettingStarted.html http://www.javalobby.org/java/forums/t17876.html
http://stackoverflow.com/questions/6913325/annotation-to-make-a-private-method-public-only-for-test-classes http://blog.solidcraft.eu/2010/10/googole-guava-v07-examples.html http://www.programcreek.com/java-api-examples/com.google.common.annotations.VisibleForTesting http://programmers.stackexchange.com/questions/100959/how-do-you-unit-test-private-methods
http://marxsoftware.blogspot.com/2011/11/two-generally-useful-guava-annotations.html