2. @Deprecated
这个注解的定义如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
从它的定义我们可以知道,它会被文档化,能够保留到运行时,能够修饰构造方法、属性、局部变量、方法、包、参数、类型。这个注解的作用是告诉编译器被修饰的程序元素已被“废弃”,不再建议用户使用。
3. @SuppressWarnings
这个注解我们也比较常用到,先来看下它的定义:
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
它能够修饰的程序元素包括类型、属性、方法、参数、构造器、局部变量,只能存活在源码时,取值为String[]。它的作用是告诉编译器忽略指定的警告信息,它可以取的值如下所示:
deprecation:忽略使用了废弃的类或方法时的警告;
unchecked:执行了未检查的转换;
fallthrough:swich语句款中case忘加break从而直接“落入”下一个case;
path:类路径或原文件路径等不存在;
serial:可序列化的类缺少serialVersionUID;
finally:存在不能正常执行的finally子句;
all:以上所有情况产生的警告均忽略。
这个注解的使用示例如下:
@SuppressWarning(value={"deprecation", "unchecked"})
public void myMethos() {...}
通过使用以上注解,我们告诉编译器忽略myMethod方法中由于使用了废弃的类或方法或是做了未检查的转换而产生的警告。
四、自定义注解
我们可以创建我们自己的注解类型并使用它。请看下面的示例:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Inherited
public @interface MethodInfo {
String author() default "absfree";
String date();
int version() default 1;
}
在自定义注解时,有以下几点需要我们了解:
注解类型是通过”@interface“关键字定义的;
在”注解体“中,所有的方法均没有方法体且只允许public和abstract这两种修饰符号(不加修饰符缺省为public),注解方法不允许有throws子句;
注解方法的返回值只能为以下几种:原始数据类型), String, Class, 枚举类型, 注解和它们的一维数组,可以为方法指定默认返回值。
我们再把上面提到过的@SuppressWarnings这个注解类型的定义拿出来看一下,这个注解类型是系统为我们定义好的,它的定义如下:
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
我们可以看到,它只定义了一个注解方法value(),它的返回值类型为String[],没有指定默认返回值。我们使用@SuppressWarnings这个注解所用的语法如下:
@SuppressWarnings(value={"value1", "value2", ...})
也就是在注解类型名称后的括号内为每个注解方法指定返回值就可以使用这个注解。下面我们来看看怎么使用我们自定义的注解类型@MethodInfo:
public class AnnotationTest {
@MethodInfo(author="absfree", date="20160410")
public static void main(String[] args) {
System.out.println("Using custom annotation...");
}
}
那么现在问题来了,我们使用的自定义注解对于编译器或是虚拟机来说是有意义的吗(编译器或是虚拟机能读懂吗)?显然我们什么都不做的话,编译器或者虚拟机是读不懂我们的自定义注解的。下面我们来介绍以下注解的解析,让编译器或虚拟机能够读懂我们的自定义注解。
五、注解的解析
1. 编译时解析
编译时注解指的是@Retention的值为CLASS的注解,对于这类注解的解析,我们只需做以下两件事:
自定义类继承 AbstractProcessor类;
重写其中的 process 函数。
实际上,编译器在编译时会自动查找所有继承自 AbstractProcessor 的类,然后调用他们的 process 方法。因此我们只要做好上面两件事,编译器就会主动去解析我们的编译时注解。现在,我们把上面定义的MethodInfo的Retention改为CLASS,我们就可以按照以下代码来解析它:
@SupportedAnnotationTypes({ "com.custom.customannotation.MethodInfo" })
public class MethodInfoProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
HashMap<String, String> map = new HashMap<String, String>();
for (TypeElement typeElement : annotations) {
for (Element element : env.getElementsAnnotatedWith(typeElement)) {
MethodInfo methodInfo = element.getAnnotation(MethodInfo.class);
map.put(element.getEnclosingElement().toString(), methodInfo.author());
}
}
return false;
}
}
@SupportedAnnotationTypes注解描述了Processor要解析的注解的名字。process 函数的annotations参数表示 表示待处理的注解集,env表示当前或是之前的运行环境。process函数的返回值表示annotations中的注解是否被这个Processor接受。
2. 运行时注解解析
首先我们把MethodInfo注解类型中Retention的值改回原来的RUNTIME,接下来我们介绍如何通过反射机制在运行时解析我们的自定义注解类型。
java.lang.reflect包中有一个AnnotatedElement接口,这个接口定义了用于获取注解信息的几个方法:
T getAnnotation(Class annotationClass) //返回该程序元素的指定类型的注解,若不存在这个类型的注解则返回null
Annotation[] getAnnotations() //返回修饰该程序元素的所有注解
Annotation[] getDeclaredAnnotations() //返回直接修饰该元素的所有注解
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) //当该程序元素被指定类型注解修饰时,返回true,否则返回false
解析我们上面的自定义注解MethodInfo的相关示例代码如下(AnnotationParser.java):
public class AnnotationParser {
public static void main(String[] args) {
try {
Class cls = AnnotationTest.class;
for (Method method : cls.getMethods()) {
MethodInfo methodInfo = method.getAnnotation(MethodInfo.class);
if (methodInfo != null) {
System.out.println("method name:" + method.getName());
System.out.println("method author:" + methodInfo.author());
System.out.println("method date:" + methodInfo.date());
System.out.println("method version:" + methodInfo.version());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行以上代码我们可以得到以下输出:
这说明我们已经成功解析了自定义注解。关于注解有点我们需要明确的是,作为描述代码本身的一种元数据,注解是一种”被动“的信息。也就是说,必须由编译器或虚拟机来“主动”解析它,它才能发挥自己的作用。