首页 > Java > 高级篇 > 泛型的反射、注解的反射
2014
09-02

泛型的反射、注解的反射

注解的反射、泛型的反射

一、泛型的反射

泛型的基础知识见:http://www.flyne.org/article/248

首先必须清楚以下概念:

  • ArrayList<E>称为泛型类型,其中的E称为类型变量
  • ArrayList<Integer>称为参数化的类型(parameterized type),其中的Integer称为实际类型参数。
  • ArrayList称为原始类型(raw type)

泛型的反射就是为了取得参数化类型(parameterized type)中的实际类型参数(actual type argument)。

1、核心API

注解的反射、泛型的反射325

Type是所有类型的公共父接口,Since 1.5。所有类型指的有:

  • 原始类型:raw type,对应Class
  • 基本类型:primitive type,对应Class
  • 参数化类型:parameterized type,对应ParameterizedType,如List<String>、Map<key,value>
  • 泛型数组:对应GenericArrayType,如Class<String>[],【还没用过……】
  • TypeVariable、WildcardType:略。

扩展:为什么要设计Type接口?(Type接口的几个子接口的作用)
① 没有泛型的时候,只有原始类型,此时所有的类型都可以通过字节码文件类Class进行抽象。
② JDK1.5中引入泛型后,扩充了数据类型,增加了参数化类型、泛型数组类型……
③ 由于存在类型擦除,编译后,与泛型有关的类型(参数化类型、泛型数组……)将全部被打回原形,因此并不存在和自身类型一致的字节码文件。为了通过反射操作这些类型以满足实际开发的需要,Java新增了ParameterizedType……几种类型来代表与泛型有关的类型。
④ 当然,为了更好的发挥面向对象的特性,最终引入了Type接口“统一”了与泛型有关的类型和原始类型。但Type接口并不提供任何方法。

补充阅读材料:http://blog.csdn.net/kobejayandy/article/details/11709043

2、泛型反射的应用:

泛型的反射紧紧抓住一条(两个步骤):① 通过Class搞到同泛型有关的类型【一般都是Type类型,需强转成四种类型】,② 再通过四个子接口中的API进行泛型反射。因此泛型反射时一定要注意Class类中返回值为Type、Type[]的getXxx()方法。

1)DAO层设计中
详见《DAO层设计(泛型反射)》一文。

public class BaseDaoImpl<T> implements BaseDao<T> {
	private Class clazz;

	public BaseDaoImpl() {
		// 利用泛型的反射在实例化时搞到实体类的字节码
		Type t = this.getClass().getGenericSuperclass();
		ParameterizedType pt = (ParameterizedType) t;
		Type actualType = pt.getActualTypeArguments()[0];
		clazz = (Class) actualType;
	}
	……
}

在DAO层的设计中,当BaseDaoImpl的子类被实例化时,BaseDaoImpl构造函数中拿到的this指向子类,获取父类的参数化类型,即BaseDaoImpl<Xxx>,再通过泛型反射拿到实体类的字节码。

2)待补充……

二、注解的反射

注解的基础知识见:http://www.flyne.org/article/243

注解的反射包括反射注解本身反射注解的属性。要完成这一过程,需借助java.lang.reflect.AnnotatedElement接口中定义的方法。

1、AnnotatedElement接口

从字面意义上来看,该接口代表了被注解的程序元素,因为程序元素可以是类、方法、字段及包,所以可以推断,Class、Method、Field等类有实现该接口。(查阅API,也是这样)

AnnotatedElement接口中定义了如下方法:

  • getAnnotation(Class<T> annotationClass):如果程序元素上存在指定类型的注解则返回该注解,否则返回null。
  • getAnnotations():返回程序元素上存在的所有注解,包括通过继承得到的注解。见@Inherited
  • getDeclaredAnnotations():返回程序元素上直接存在的注解。
  • isAnnotationPresent(Class<T> annotationClass):是否存在指定类型的注解

2、元注解

注解的注解,即加在注解上的注解,为注解服务。

1) @Retention:加在自己定义的注解上指示注解能保留多久,默认为RetentionPolicy.CLASS

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention{
    RetentionPolicy value();
}

注解中属性的类型必须是:基本类型、String、Class、注解、枚举及以上类型的一维数组。因此断定RetentionPolicy为枚举类型。有SOURCE、CLASS、RUNTIME等枚举值。如果需要自定义的注解能被反射到,需设置RetentionPolicy.RUNTIME。

2)@Target:限定注解适用的程序元素

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    ElementType[] value();
}

不难推断ElementType也为枚举类型,取值有:ANNOTATION_TYPE、FILED、METHOD、TYPE。

3)@Documented:是否在生成的Java doc上显示注解

4)@Inherited:使用了注解的类,其子类是否可以继承父类使用的注解

3、模拟JUnit中的@Test

注解的灵魂不在于定义,而是对出现注解的地方进行反射处理。本节例子中模拟了Junit中的@Test注解(没有图形化,仅用控制台打印结果),完成的功能有:

  • 执行有@MyTest注解的方法
  • 如果有@MyTest(time=100)注解,则执行时间超过100毫秒,打印出“超时了”

先看@MyTest注解的定义和注解的使用:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyTest {
	long time() default -1;
}

public class MyTestDemo {
	@MyTest
	public void test1(){
		System.out.println("被测试方法1");
	}
	@MyTest(time=100)
	public void testUpdate(){
		try {
			Thread.sleep(1000);
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("被测试方法2");
	}
}

1)反射注解:执行有@MyTest注解的方法

public static void main(String[] args) throws Exception {
	Class clazz = MyTestDemo.class;
	Method[] ms = clazz.getMethods();
	for(Method m : ms){
		if(m.isAnnotationPresent(MyTest.class)){
			m.invoke(clazz.newInstance(), null);
		}
	}
}

2)反射注解的属性:根据time属性判断是否超时

public static void main(String[] args) throws Exception{
	Class clazz = MyTestDemo.class;
	Method[] ms = clazz.getMethods();
	for (Method m : ms) {
		if (m.isAnnotationPresent(MyTest.class)) {
			MyTest mt = m.getAnnotation(MyTest.class);
			if(mt.time() != -1){
				long start = System.currentTimeMillis();
				m.invoke(clazz.newInstance(), null);
				long end = System.currentTimeMillis();
				if((end-start)>mt.time()){
					System.out.println("超时了");
				}
			}else{
				m.invoke(clazz.newInstance(), null);
			}
		}
	}
}

留下一个回复

你的email不会被公开。