什么是注解
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.
注解是一种元数据,提供代码的信息,但不是代码的一部分,对被注解的代码的运行没有影响。
这里提到的元数据是描述数据的数据,结合实例来说明:
<string name="app_name">AnnotionDemo</string>
这里的"app_name"就是描述数据"AnnotionDemo"的数据。
public class MainActivity extends AppCompatActivity {
@InjectView(R.id.tv)
TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Views.inject(this); // 利用反射获取注解的信息
}
}
注解的作用
Annotations have a number of uses, among them:
-
Information for the compiler — Annotations can be used by the compiler to detect errors or suppress warnings.
-
Compile-time and deployment-time processing — Software tools can process annotation information to generate code, XML files, and so forth.
-
Runtime processing — Some annotations are available to be examined at runtime.
-
为编译器提供信息 —— 比如被 @Override 标记的方法如果不是父类的某个方法,IDE 会报错
-
编译时和部署时处理 —— 软件工具可以使用注解信息生成代码、XML文件等
-
运行时处理 —— 比如第三方框架xUtils,通过注解 @ViewInject 减少对 findViewById 的调用
注解是如何工作的?
注解仅仅是元数据,和业务逻辑无关,所以当你查看注解类时,发现里面没有任何逻辑处理
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectView {
int value();
}
如果注解不包含业务逻辑处理,必然有人来实现这些逻辑。注解的逻辑实现是元数据的用户来处理的,注解值提供信息,由注解的用户读取这些信息并实现必要的逻辑。
当使用java中的注解时(比如@Override、@Deprecated、@SuppressWarnings)JVM就是用户。
如果是自定义的注解,它的用户是每个使用注解的类,在被注解的类中通过反射来获取注注解的信息。
比如使用注解简化 findViewById
的操作
public class MainActivity extends AppCompatActivity {
@InjectView(R.id.tv)
TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Views.inject(this); // 利用反射获取注解的信息
}
}
Views.inject(this)
的实现
public class Views {
public static void inject(Activity activity) {
Class<? extends Activity> activityClass = activity.getClass();
Field[] fields = activityClass.getDeclaredFields();
for (Field field : fields) {
InjectView injectViewAnnotation = field.getAnnotation(InjectView.class);
if (injectViewAnnotation != null) {
View view = activity.findViewById(injectViewAnnotation.value());
try {
field.setAccessible(true);
field.set(activity, view);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
注解的元注解
通过阅读注解类的源码可以发现,任何一个注解类都有如下特征:
- 注解类会被 @interface 标记
- 注解类的顶部会被 @Documented、@Retention、@Target、@Inherited这四个注解标记(@Target必须要有,其他可选)
@Target
@Target annotation marks another annotation to restrict what kind of Java elements the annotation can be applied to.
@Target 用来约束该注解的使用范围。主要有几个
ElementType.ANNOTATION_TYPE can be applied to an annotation type.
ElementType.CONSTRUCTOR can be applied to a constructor.
ElementType.FIELD can be applied to a field or property.
ElementType.LOCAL_VARIABLE can be applied to a local variable.
ElementType.METHOD can be applied to a method-level annotation.
ElementType.PACKAGE can be applied to a package declaration.
ElementType.PARAMETER can be applied to the parameters of a method.
ElementType.TYPE can be applied to any element of a class.
- CONSTRUCTOR 构造函数
- FIELD 实例变量
- LOCAL_VARIABLE 局部变量
- METHOD 方法
- PACKAGE 包
- PARAMETER 参数
- TYPE 用于描述类、接口(包括注解类型) 或enum声明
@Retention
@Retention @Retention annotation specifies how the marked annotation is stored:
- RetentionPolicy.SOURCE – The marked annotation is retained only in the source level and is ignored by the compiler.
- RetentionPolicy.CLASS – The marked annotation is retained by the compiler at compile time, but is ignored by the Java Virtual Machine (JVM).
- RetentionPolicy.RUNTIME – The marked annotation is retained by the JVM so it can be used by the runtime environment.
表示需要在什么级别保存该注解信息,用于描述注解的生命周期,即被描述的注解在什么范围内有效
- SOURCE 在源文件中有效,即源文件保留;
- CLASS 在class文件中有效,即 class 保留;
- RUNTIME 在运行时有效,即运行时保留;
@Documented
@Documented annotation indicates that whenever the specified annotation is used those elements should be documented using the Javadoc tool. (By default, annotations are not included in Javadoc.)
可以用来生成 Javadoc 文档
@Inherited
@Inherited annotation indicates that the annotation type can be inherited from the super class. (This is not true by default.) When the user queries the annotation type and the class has no annotation for this type, the class' superclass is queried for the annotation type. This annotation applies only to class declarations.
用于描述某个被标注的类型是可被继承的
自定义注解
格式
元注解
public @interface 注解名 {
定义体;
}
特别说明
- 注解类中的方法只能用 public 或者默认这两个访问权修饰,不写public就是默认
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitColor {
public enum Color{ BULE,RED,GREEN};
Color fruitColor() default Color.GREEN;
}
···
2. 如果注解类中只有一个成员,最好把方法名设置为"value",比如
```java
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitName {
String value() default "";
}
- 注解元素必须有确定的值,要么在定义注解的默认值中指定,要么在使用注解时指定,非基本类型的注解元素的值不可为 null。因此, 使用空字符串或 0 作为默认值是一种常用的做法。
实战
自定义注解 ToDo.java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface Todo {
public enum Priority {LOW, MEDIUM, HIGH}
public enum Status {STARTED, NOT_STARTED}
String author() default "Yash";
Priority priority() default Priority.LOW;
Status status() default Status.NOT_STARTED;
}
使用注解的类 BusinessLogic
public class BusinessLogic {
public BusinessLogic() {
super();
}
public void compltedMethod() {
System.out.println("This method is complete");
}
@Todo(priority = Todo.Priority.HIGH)
public void notYetStartedMethod() {
// No Code Written yet
}
@Todo(priority = Todo.Priority.MEDIUM, author = "Uday", status = Todo.Status.STARTED)
public void incompleteMethod1() {
//Some business logic is written
//But its not complete yet
}
@Todo(priority = Todo.Priority.LOW, status = Todo.Status.STARTED )
public void incompleteMethod2() {
//Some business logic is written
//But its not complete yet
}
}
注解的用户 TodoReport
public class TodoReport {
public TodoReport() {
super();
}
public static void main(String[] args) {
getTodoReportForBusinessLogic();
}
/**
* 解析使用注解的类,获取通过注解设置的属性
*/
private static void getTodoReportForBusinessLogic() {
Class businessLogicClass = BusinessLogic.class;
for(Method method : businessLogicClass.getMethods()) {
Todo todoAnnotation = (Todo)method.getAnnotation(Todo.class);
if(todoAnnotation != null) {
System.out.println(" Method Name : " + method.getName());
System.out.println(" Author : " + todoAnnotation.author());
System.out.println(" Priority : " + todoAnnotation.priority());
System.out.println(" Status : " + todoAnnotation.status());
System.out.println(" --------------------------- ");
}
}
}
}
运行结果
Method Name : notYetStartedMethd
Author : Yash
Priority : HIGH
Status : NOT_STARTED
--------------------------
Method Name : incompleteMethod2
Author : Yash
Priority : LOW
Status : STARTED
--------------------------
Method Name : incompleteMethd1
Author : Uday
Priority : MIDUM
Status : STARTED
--------------------------
总结
- 注解是一种元数据
- 注解的作用
- 注解和业务逻辑无关,需要有个类来读取注解的信息
- 注解的元注解
- 如何实现自定义的注解
网友评论