JavaSE第七十一讲:Target及ElementType详解

1. 继续上一讲内容,复习上一讲内容我们讲到了 Retention以及RetentionPolicy。这两个都是成对出现的,因为Retention里面包含了一个属性value类型为 RetentionPolicy 枚举类型,它有三个枚举CLASS、RUNTIME、SOURCE。分别表示注解产生在三种不同的环境下。

2. 下面我们学习一下,如果在RUNTIME情况下,如何利用反射来读取已经Class中的注解信息。

定义Annotation时必须设定RetentionPolicy为RUNTIME,也就是可以在VM中读取Annotation信息。

1) 定义注解 MyAnnotation.java

package com.ahuier.annotation;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/*
 * 使用Retention修饰这个注解,
 * 告诉这个注解在使用的时候即可以用到.Class文件,又能在运行过程中通过反射的方式读取出来
 * 这个注解用在Mytest类 和 它里面的方法output()方法上
 */
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
	String hello() default "ahuier";
	String world();
}
2) 定义一个类使用上面定义的注解
package com.ahuier.annotation;

@MyAnnotation(hello = "beijing", world = "shanghai")
public class MyTest {
	
	@MyAnnotation(hello = "tianjin", world = "shangdi")
	@Deprecated
	@SuppressWarnings("unchecked")       //一个方法可以有多个注解所修饰
	public void output(){
		System.out.println("output something!");
	}
}
3) 定义一个类利用反射来取得注解中的信息

package com.ahuier.annotation;

import java.lang.reflect.Method;

/*
 * 通过反射获取类MyTest中output()方法上所有的信息,并且把信息显示出来
 */
public class MyReflection {
	public static void main(String[] args) throws Exception{
		MyTest myTest = new MyTest();
		Class<MyTest> c = MyTest.class;
		
		/*
		 * 通过反射获取method对象,由于Method实现了AnnotatedElement接口,所以它可以调用获取注解信息的API来获得注解信息。
		 */
		Method method = c.getMethod("output", new Class[]{}); 
		
		/*
		 * 查看JDk Doc 文档,在Method类的父类AccessibleObject类中isAnnotationPresent()
		 * 判断某一个方法上面是否存在某一个注解
		 */
		if(method.isAnnotationPresent(MyAnnotation.class)){
			method.invoke(myTest, new Object[]{}); //如果存在这个注解,则去调用MyTest类中output()方法
		}				
	}
}
编译执行结果:

output something!

【说明】:以上输出方法内容,是因为我们在定义注解的时候,将注解信息 Retention 设置为“RUNTIME”

为了更好的理解注解信息三个枚举的不同,我们将注解 MyAnnotation.java 中的注解 Retention 设置为"CLASS" 和 "SOURCE" 如下代码所示:

@Retention(RetentionPolicy.CLASS)
public @interface MyAnnotation {
	String hello() default "ahuier";
	String world();
}
@Retention(RetentionPolicy.SOURCE)
public @interface MyAnnotation {
	String hello() default "ahuier";
	String world();
}
编译执行结果都没输出任何内容,因为设置为这两个类型的枚举值之后,它们就不会在JVM运行期保留这些注解信息,所以即便通过反射方式也是无法获得他们的信息的,也就是在执行 MyReflection.java这个类的 if(method.isAnnotationPresent(MyAnnotation.class))时候,它返回false,不执行里面的内容了。


通过上面一个例子我们可以知道使用RUNTIME之后,是可以通过反射的方式调用到被注解修饰的方法,现在我们可以通过它们的一些API来获得注解的一些某一个方法上面特定的注解信息。

对上面第三个代码 MyReflection.java 继续写下去如下:

package com.ahuier.annotation;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

/*
 * 通过反射获取类MyTest中output()方法上所有的信息,并且把信息显示出来
 */
public class MyReflection {
	public static void main(String[] args) throws Exception{
		MyTest myTest = new MyTest();
		Class<MyTest> c = MyTest.class;
		
		/*
		 * 通过反射获取method对象,由于Method实现了AnnotatedElement接口,所以它可以调用获取注解信息的API来获得注解信息。
		 */
		Method method = c.getMethod("output", new Class[]{}); 
		
		/*
		 * 查看JDk Doc 文档,在Method类的父类AccessibleObject类中isAnnotationPresent()
		 * 判断某一个方法上面是否存在某一个注解
		 */
		if(method.isAnnotationPresent(MyAnnotation.class)){
			method.invoke(myTest, new Object[]{}); //如果存在这个注解,则去调用MyTest类中output()方法
			/*
			 * 查看JDK Doc中Method类的public <T extends Annotation> T getAnnotation(Class<T> annotationClass)方法。
			 * 如果注解存在,则放回这个注解,否则返回为空
			 */
			MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);
			String hello = myAnnotation.hello(); //获取 MyAnnotation中的 hello()注解信息
			String world = myAnnotation.world(); //获取 MyAnnotation中的 world()注解信息
			System.out.println(hello + ", " + world);
		}
		/*
		 * 查看JDK Doc文档中 public Annotation[] getAnnotations() 
		 * 放回所有在特定元素上的注解,如果这个元素上没有注解,则返回一个数组长度为0的数组,而非返回空的数组,所以不需要判断数组是否为空,直接遍历数组
		 */
		Annotation[] annotations = method.getAnnotations();
		for(Annotation annotation : annotations){
			
			/*
			 * 查看JDK文档Annotation接口的 Class<? extends Annotation> annotationType()
			 * 返回它所对应的Class对象
			 * 以下输出:
			 * com.ahuier.annotation.MyAnnotation 
			 * java.lang.Deprecated
			 * 原因是MyTest中的output()上面的定义的三个注解中有两个注解RetentionPolicy为“RUNTIME”,如果是“CLASS” 和 "SOURCE",则得不到的
			 */
			System.out.println(annotation.annotationType().getName()); 
		}

	}
}
编译执行结果:

output something!
tianjin, shangdi
com.ahuier.annotation.MyAnnotation
java.lang.Deprecated


3. 下面我们学习Target,限定annotation使用对象@Target,如下图所示:

  

这边的@Target与Retention有异曲同工之妙,Retention搭配RetentionPolicy来指定它的枚举值是什么。而Target也搭配ElementType表示这注解可以修饰哪些目标。

查看JDK Doc文档Target,它有一个value,是ElementType 枚举类型,查看ElementTyped,它的枚举值如下所示:


ANNOTATION_TYPE: 注解只能修饰注解,不能修饰其他的东西

CONSTRUCTOR: 注解只能修饰构造方法

FIELD: 注解只能修饰属性(成员变量)

LOCAL_VARIABLE: 注解只能修饰局部变量

METHOD: 注解只能修饰方法

PACKAGE: 注解只能修饰包

PARAMETER: 注解只能修饰方法的参数

TYPE: 注解只能修饰类、接口、枚举

如下图所示:

下面我们来通过例子学习上面的内容:

1) 定义一个注解MyTarget.java

package com.ahuier.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

/*
 * @Target(ElementType.METHOD)表示这个注解只能用来修饰方法
 */
@Target(ElementType.METHOD)
public @interface MyTarget {
	String value();
}
2) 定义一个类使用这个注解,如下图所示提示错误


提示错误原因是在MyTarget这个注解中我们定义的注解只能用来修饰方法,所以在修饰类的时候会提示错误。修改的方法是将MyTarget.java注解Target的value改为TYPE则可以,如下代码所示:

@Target(ElementType.TYPE)
public @interface MyTarget {
	String value();
}

4. 要求为API文件@Documented


定义一个带@Documented的注解

package com.ahuier.annotation;

import java.lang.annotation.Documented;

/*
 * @Documented 表示带@Documented修饰的东西会添加到生成文档的API中去
 */

@Documented
public @interface DocumentedAnnotation {
	String hello();
}

定义一个使用这个注解的类

package com.ahuier.annotation;

public class DocumentedTest {
	
	@DocumentedAnnotation(hello = "welcome")
	public void method(){
		System.out.println("hello world");
	}
}
编译生成Java DOC文档:Eclipse --> Project --> Generate Javadoc --> 选择需要生成doc的包

生成完成打开生成API文档,查看我们定义的method()方法如下图所示,已经被标注了注解信息了。




5. 子类是否继承父类@Inherited,如下图所示:



到此为止,我们的注解已经全部讲完了,在实际开发中,我们一般很少自己去定义注解,但是通过这些内容我们可以明白注解的本源是什么,可以在以后的实际开发中更好的学习好注解。







  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值