Java编程思想学习笔记——注解
前言
在 Android 开发的过程中,我们为了减少重复代码的编写,会使用类似ButterKnife,AndroidAnnotations
这类依赖注解库。代码示例如下:
// 不使用
Button btn = (Button)findViewById(R.id.btn);
// 使用 ButterKnife
@Bind(R.id.btn)
Button btn;
// 使用 AndroidAnnotations
@ViewById(R.id.btn)
Button btn;
可以看出通过注解,我们能大量减少啰嗦的声明和强转类型的代码的编写。
通过本篇文章的学习,对注解有个基本的认识和了解。
正文
1. 什么是注解
注解(也叫元数据)为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻非常方便地使用这些数据。
2. 注解的优点
- 1. 可以提供用来完整地描述程序所需的信息(这些信息无法用 Java 表达)-> 以将由编译器来测试和验证的格式存储有关程序的额外信息。
- 2. 可以用来生成描述符文件或是新的类的定义 -> 减轻编写 "样板" 代码的负担
- 3. 更加干净易读的代码以及编译器类型检查
3. 注解的语法
@符号的使用,其他和 Java 固有语法一样
Java SE5 内置了三种,定义在 java.lang 中的注解:
@Override 当前的方法定义将覆盖父类(超类)中的方法。
@Deprecated 被注解的元素被取代,不推荐使用 ( http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/deprecation/deprecation.html)
@SuppressWarnings 关闭不当的编译器警告信息
Java 提供四种注解,专门负责新注解的创建。这种注解叫做元注解。
- 1.@Target 表示该注解可以用于什么地方。
ElementType 参数包括:
public enum ElementType {
// 类、接口(包括注解类型)或是 enum 声明
TYPE,
// 域声明 (包括 enum 实例)
FIELD,
// 方法声明
METHOD,
// 参数声明
PARAMETER,
// 构造器声明
CONSTRUCTOR,
// 局部变量声明
LOCAL_VARIABLE,
// 注解类型声明
ANNOTATION_TYPE,
// 包声明
PACKAGE,
// 类型参数声明(Java1.8 开始使用)
TYPE_PARAMETER,
// 类型使用(Java1.8 开始使用)
TYPE_USE
}
- 2.@Retention 表示需要在什么级别保存该注解信息。
RetentionPolicy 参数包括
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
* 注解将被编译器丢弃
*/
SOURCE,
<span class="hljs-comment">/**
* 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.
*注解被编译器保存在类文件中。但在运行的时候没有被VM保存
*/</span>
CLASS,
<span class="hljs-comment">/**
* 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.
*
* <span class="hljs-doctag">@see</span> java.lang.reflect.AnnotatedElement
* 注解将被编译器保存在类文件中,在运行的时候也会被VM保存。因此可通过反射机制读取注解的信息
*/</span>
RUNTIME
}
-
3.@Documented 将此注解包含在 Javadoc 中
-
4.@Inherited 允许子类继承父类中的注解
定义注解的语法
package com;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
Created by JohnTsai on 15/11/7.
*/
public Test {
}
注解的定义很像一个空的接口。定义注解时,会需要一些元注解(meta-annotation,如上所示)
在注解中,一般都会包含一些元素以表示某些值。分析处理注解时,程序或工具可以利用这些值。
注解的元素看起来就像接口的方法,唯一的区别是可以为它设置默认值。
package com;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
- Created by JohnTsai on 15/11/7.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UseCase {
public int id();
//default 设置默认值
public String description() default "no description";
}
使用注解的语法
package com;
/**
Created by JohnTsai on 15/11/7.
*/
public class Testable {
public void execute(){
System.out.println("Executing...");
}
void testExcute(){
execute();
}
}
注解的元素在使用时表现为名 - 值对的形式。
package com;
/**
Created by JohnTsai on 15/11/7.
/
public class PasswordUtils {
(id = 47, description = "Passwords must cantain at least one numeric!")
public boolean validatePassword(String password) {
return (password.matches("\w\d\w*"));
}48)
(id =
public String encryptPassword(String password) {
return new StringBuilder(password).reverse().toString();
}
}
4. 什么时候使用注解
每当创建描述符性质的类或接口时,如果包含了重复性的工作,就可以考虑使用注解来简化与自动化该过程。
5. 编写注解处理器
使用注解很重要的一部分就是创建与使用注解处理器。Java SE5 拓展了反射机制的 API(构造这类工具),还提供了外部工具 apt 帮助我们解析带有注解的 Java 源代码。
package com;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
-
Created by JohnTsai on 15/11/8.
*/
public class UseCaseTracker {
public static void trackUseCases(List<Integer> useCases,Class<?> clz){
for(Method m :clz.getDeclaredMethods()){
UseCase useCase = m.getAnnotation(UseCase.class);
if(useCase!=null){
System.out.println(useCase.id()+useCase.description());
useCases.remove(new Integer(useCase.id()));
}
}
for(int i:useCases){
System.out.println("Warning:Missing use case -"+i);
}
}
public static void main(String[] args) {
List<Integer> useCases = new ArrayList<>();
Collections.addAll(useCases,47,48,49);
trackUseCases(useCases,PasswordUtils.class);
}
}
//output
/*
47Passwords must cantain at least one numeric!
48no description
Warning:Missing use case -49
*/
使用了两个反射的方法:getDeclaredMethods()和 getAnnotation()(这两个方法都属于 AnnotatedElement 接口,Class,Method 和 Field 等类都实现了这个接口)
注解元素
注解元素可用的类型如下:
1.所有的基本类型(如int,float,boolean等等)
2.String
3.Class
4.enum
5.Annotation
6.以上类型的数组
默认值限制
编译器对元素的默认值有限制。
不能有不确定的值
( 即要么具有默认值,要么使用注解时提供的元素的值)
我们可以查看 AndroidAnnotations 中的 @ViewById 的源码,查看它的写法,就能看出默认值的写法:
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface ViewById {
int value() default ResId.DEFAULT_VALUE;
String <span class="hljs-title function_">resName</span><span class="hljs-params">()</span> <span class="hljs-keyword">default</span> <span class="hljs-string">""</span>;
}