`
lsy
  • 浏览: 122981 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

提炼Java Reflection

阅读更多

反射是Java语言中很重要的一个组成部分,所以就此话题讨论的资源可谓数之不尽,日常开发也会经常使用到关于反射的Reflection API。Java5.0 Tiger出现以后,更对反射API有了新的扩展,尽管讨论的话题很多,不过我还是觉得不够全面,尤其是对泛型这一块,所以就我所知,再花力气总结一番

首先反射的入口是从Class开始的,所以如何获取Class就变得十分关键了。这里总结了几种方式:
1.通过${name}.class 语法。这里${name}可以是对象,也可以是原始数据类型,不过别忘了void.class和Void.class
2.通过${name}.TYPE 语法。这里${name}是八种原始数据的包装类和Void.TYPE
3.通过对象的getClass()方法。
4.通过Class对象的forName()方法
5.通过类Class的getSuperclass()获取父亲类Class
6.通过类Class的getEnclosingClass()获取外部类Class
7.通过类Class的getClasses()和getDeclaredClasses()获取内部类Class
下面是一张表用来说明getClasses()和getDeclaredClasses()两个方法,稍后还会用该表说明其他Reflection API
--------------------------------------------------------------------------------------------
Member       | Class API                           | Return type | Inherited members | Private members
--------------------------------------------------------------------------------------------
Class            | getDeclaredClasses()         | Array    | N        | Y
                    | getClasses()                       | Array   | Y          | N
--------------------------------------------------------------------------------------------
Field             | getDeclaredField()              | Single  | N         | Y
                    | getField()                            | Single  | Y         | N
                    | getDeclaredFields()            | Array    | N         | Y
                    | getFields()                          | Array    | Y         | N
---------------------------------------------------------------------------------------------
Method         | getDeclaredMethod()         | Single   | N        | Y
                    | getMethod()                       | Single   | Y         | N
                    | getDeclaredMethods()        | Array    | N        | Y
                    | getMethods()                      | Array    | Y        | N
---------------------------------------------------------------------------------------------
Constructor  | getDeclaredConstructor()   | Single  |   N/A   | Y
                    | getConstructor()                  | Single |  N/A    | N
                    | getDeclaredConstructors()  | Array   |  N/A    | Y
                    | getConstructors()                | Array  |   N/A    | N
---------------------------------------------------------------------------------------------
表一: 成员方法对照表
如表一所示,getClasses()拥有继承的特点,可以获取父亲级定义的内部类,而不能访问定义为private的内部类;而getDeclaredClasses()刚好相反,可以访问定义为private的内部类,却无法获取父亲级定义的内部类

成功获取了Class以后,那么就可以开始访问Field,Method和Constructor了,他们都继承自java.lang.reflect.Member。从上表已经很容易可以看出各个成员是否拥有继承特性,是否能够访问私有成员,返回类型的数量这些信息。这里需要注意一点,由于Constructor是无法被继承的,所以Constructor成员任何方法都没有继承的特性。另外Field和Method在赋值或者调用的之前需要留意是否在操作私有成员,如果是那么需要先修改可访问度,执行setAccessible(true)。还有一点就是Method成员的getDeclaredMethod()和getMethod()方法都需要两个参数,一个是方法的名称,另外一个参数Class的数组,这里需要感谢Java5.0引入不定长参数的特点,使我们可以在某些情况下少传入一个参数,如:
假设Order类有下列方法

public Long getId() { return id; }


5.0以前获取该方法的代码如下

Method getId = Order.class.getMethod("getId", new Class[0]);


而5.0仅需要写

Method getId = Order.class.getMethod("getId");



现在说说5.0 泛型出现之后,Java Reflection API的新特点。首先增加一个接口java.lang.reflect.Type,其下一共有4个接口继承了它,TypeVariable,ParameterizedType,GenericArrayType和WildcardType,下面逐个分析。
1.TypeVariable
我们知道泛型信息会在编译时被JVM编译时转换为定义的一个特定的类型,这减少了应用程序逐步向下检查类型的开支,避免了发生ClassCastException的危险。而TypeVariable就是用来反映在JVM编译该泛型前的信息。举个例子,假设BaseOrder类定义有如下一个方法

public class BaseOrder<M extends Object & Serializable,N extends Comparable<N>> implements IBaseOrder {
	public M getManufactory(){
		return manufactory;
	}
}


这时候我们可以通过如下代码获取该泛型Type,并且经过测试该Type就是TypeVariable

Field manufactoryField = BaseOrder.class.getDeclaredField("manufactory");	
type = manufactoryField.getGenericType();
assertTrue("The type of field manufactory is an instance of TypeVariable", type instanceof TypeVariable);
TypeVariable tType = (TypeVariable)type;
assertEquals("The name of this TypeVariable is M", "M", tType.getName());
assertEquals("The TypeVariable bounds two type", 2, tType.getBounds().length); 
assertEquals("One type of these bounds is Object", Object.class, tType.getBounds()[0]); 
assertEquals("And annother si Serializable", Serializable.class, tType.getBounds()[1]);


通过getName()方法可以获取该泛型定义的名称,而更为重要的是getBounds()方法,可以判断该泛型的边界。

2.ParameterizedType
这个接口就比较出名了,在过去讨论最多的问题就是GenericDao<T>中,如何获取T.class的问题了。这里再翻出来过一遍,加入上述的类BaseOrder定义不变,新定义一个Order对象,代码如下:

public class Order
		extends BaseOrder<Customer, Long> implements IOrder, Serializable {
}


那么如何通过Order获取到Customer呢?

Type genericSuperclass = Order.class.getGenericSuperclass();
assertTrue("Order's supper class is a type of ParameterizedType.", genericSuperclass instanceof ParameterizedType);
ParameterizedType pType = (ParameterizedType)genericSuperclass;
assertEquals("Order's supper class is BaseOrder.", BaseOrder.class, pType.getRawType());
Type[] arguments = pType.getActualTypeArguments();
assertEquals("getActualTypeArguments() method return 2 arguments.", 2, arguments.length);
for (Type type : arguments) {
	Class clazz = (Class)type;
	if(!(clazz.equals(Customer.class)) && !(clazz.equals(Long.class))){
		assertTrue(false);
	}
}


可以看出通过Order类的getGenericSuperclass()方法将返回一个泛型,并且它就是ParameterizedType。这个接口的getRawType()方法和getActualTypeArguments()都非常重要,getRawType()方法返回的是承载该泛型信息的对象,而getActualTypeArguments()将会返回一个实际泛型对象的数组。这里先提及一下Class对象中getGenericSuperclass()和getSuperclass()两个方法的区别,后文还有详细说明。getGenericSuperclass()方法首先会判断是否有泛型信息,有那么返回泛型的Type,没有则返回Class,方法的返回类型都是Type,这是因为Tiger中Class也实现了Type接口。将父亲按照Type接口的形式返回,而getSuperclass()直接返回父亲的Class。

3.GenericArrayType
这个接口比较好理解。如果泛型参数是一个泛型的数组,那么泛型Type就是GenericArrayType,它的getGenericComponentType()将返回被JVM编译后实际的数组对象。这里假设上文中BaseOrder有一个方法如下:

public String[] getPayments(String[] payments, List<Product> products){
	return payments;
}


可以看出该方法的参数中有泛型信息,测试一下:

Method getPayments = BaseOrder.class.getMethod("getPayments", new Class[]{String[].class, List.class});
types = getPayments.getGenericParameterTypes();
assertTrue("The first parameter of this method is GenericArrayType.", types[0] instanceof GenericArrayType);
GenericArrayType gType = (GenericArrayType)types[0];
assertEquals("The GenericArrayType's component is String.", String.class, gType.getGenericComponentType());


发现这个getPayments()方法中的一个参数String[] payments是一个GenericArrayType,通过getGenericComponentType()方法返回的是String.class。这是怎么回事呢?这里我们回过头去看Class对象的getGenericSuperclass()方法和getSuperclass()方法,如果把它们说成是一对的话,那么这里的getGenericParameterTypes()和getParameterTypes()就是另外一对。也就是说getGenericParameterTypes()首先判断该方法的参数中是否有泛型信息,有那么返回泛型Type的数组,没有那么直接按照Class的数组返回;而getParameterTypes()就直接按照Class的数组返回。非常相似吧,其原因就是这些成对的方法都有一个共同点就是判断是否有泛型信息,可以查看Tiger的源代码:

    public Type[] getGenericParameterTypes() {
	if (getGenericSignature() != null)
	    return getGenericInfo().getParameterTypes();
	else
	    return getParameterTypes();
    }


而这类成对出现的方法还很多,如Method对象定义的getGenericReturnType()和getReturnType(),getGenericExceptionTypes()和getExceptionTypes(),Field对象定义的getGenericType()和getType()。

4.WildcardType
这个接口就是获取通配符泛型的信息了。这里假设上述的BaseOrder定义有一个属性

private Comparable<? extends Customer> comparator;


现在就来获取泛型?的信息,测试代码如下:

Field comparatorField = BaseOrder.class.getDeclaredField("comparator");		
ParameterizedType pType = (ParameterizedType)comparatorField.getGenericType();
type = pType.getActualTypeArguments()[0];
assertTrue("The type of field comparator is an instance of ParameterizedType, and the actual argument is an instance of WildcardType.", type instanceof WildcardType);
WildcardType wType = (WildcardType)type;
assertEquals("The upper bound of this WildcardType is Customer.", Customer.class, wType.getUpperBounds()[0]);


首先我们获取到comparator这个属性,通过它的getGenericType()方法我们拿到了一个Type,可以看出它是一个ParameterizedType,而ParameterizedType的actual argument就是我们需要的WildcardType,这个接口有两个主要的方法,getLowerBounds()获取该通配符泛型的下界信息,相反getUpperBounds()方法获取该通配符泛型的上界信息。

说完了这四个接口,我们再回过头来看看Method对象,它还定义有一个方法getTypeParameters(),这又是干什么的呢?我们知道泛型是不能出现在静态的成员,静态的方法,或者静态的初始化器的逻辑中的。如下列代码都是错误的:

//error
private static T customer; 

//error
public static T getCustomer(){
	return customer;
}

//error
static {
	customerHolder = new HashSet<T>();
}


不过静态的方法的参数却是可以被泛化的,如:

public static <B extends BusinessType, S extends Serializable> Customer getSpcialCustomer(List<B> types, S serial){
	return new Customer();
}


这个方法我们就可以通过getTypeParameters()方法来获取它的参数化信息,代码如下:

Method getSpcialCustomer = SalePolicy.class.getMethod("getSpcialCustomer", new Class[]{List.class,Serializable.class});
types = getSpcialCustomer.getTypeParameters();
assertEquals("The method declared two TypeVariable.", 2, types.length);
assertEquals("One of the TypeVariable is B.", "B", ((TypeVariable)types[0]).getName());
assertEquals("And another is S.", "S", ((TypeVariable)types[1]).getName());



最后,说一下Annotation,成员可以通过isAnnotationPresent(annotationClass)和getAnnotation(annotationClass)方法来判断是否被某个Annotation标注,不过需要注意的时该annotation自身必须被标注为@Retention(RetentionPolicy.RUNTIME)。

 

晕,一写就那么晚了,抓紧睡觉去。如果本文写的有错误的地方请指正,以免误人子弟。

 

PS:附件是一个较为全面的测试代码

 

分享到:
评论
4 楼 陌路人丁 2018-11-07  
给力,爱你~
3 楼 szjszj 2015-08-08  
pType = (ParameterizedType)BankAccount.class.getGenericSuperclass();
GenericArrayType gType = (GenericArrayType)pType.getActualTypeArguments()[0];
这里报错楼主java.lang.Class cannot be cast to java.lang.reflect.GenericArrayType
2 楼 tianhandigeng 2015-04-15  
写得挺好的
1 楼 lsy 2008-07-30  
反射虽然功能强大,可以让应用程序不知情的情况下改变对象的行为和状态。不过也正因为如此,所以如果稍有疏忽可能会造成一些莫名其妙的问题。因此如果反射的目标应用于完全符合JavaBeanhttp://java.sun.com/docs/books/tutorial/javabeans/规范的对象时,那么JavaBean已经提供了一套完整的机制来处理。BeanUtils和Spring也都是这样做的,在应用时我们也不妨借鉴借鉴:idea:

相关推荐

    java的28个学习目标

    根据给定文件的信息,我们可以提炼出以下详细的Java学习目标及相关知识点: ### 1. 面向对象设计与分析(OOA/OOD)及模式 深入理解并掌握面向对象的设计原则,包括GOF(Gamma、Helm、Johnson、Vlissides)设计模式...

    java语言教学课件 大学java语言课程

    以下是根据标题和描述提炼出的Java语言核心知识点: 1. **Java简介**:Java是由Sun Microsystems(现已被Oracle收购)于1995年推出的面向对象的编程语言,它的设计目标是“一次编写,到处运行”(Write Once, Run ...

    JAVA常用英文

    以下是从“JAVA常用英文”这一主题中提炼出的详细知识点,旨在帮助理解和运用这些基础且重要的术语。 ### abstract 抽象类或方法,用于定义一种模板,允许子类继承并实现其功能。在Java中,`abstract`关键字不能...

    Java习惯用法总结编程小技巧共14页.pdf.zip

    以下是根据标题和描述提炼出的一些关键知识点: 1. **代码风格与格式**:良好的编程习惯始于整洁的代码,包括合理的缩进、空格使用、注释清晰等。Java有标准的编码规范,如Oracle的Java Code Conventions,遵循这些...

    JAVA基础知识总结&#40;超级经典&#41;.doc

    Java是世界上最流行的编程语言之一,尤其在企业级应用开发中占据主导地位。...以上就是从文档标题和描述中提炼出的Java基础知识点,它们构成了学习Java的基础,并为更高级的开发奠定了坚实的基础。

    Thinking_in_Java_4th_Edition_CN

    11. **反射(Reflection)**:Java的反射机制允许在运行时检查类的信息并动态调用方法,这在元编程和框架开发中非常有用。 12. **异常处理(Exception Handling)**:学习如何捕获和处理程序运行时可能出现的错误,...

    java 开发与应用

    ### Java开发与应用知识点详解 ...以上是根据给定文件中的标题、描述、标签以及部分内容提炼和扩展出的相关知识点。希望这些内容能够帮助您更好地理解和掌握Java开发与应用的核心概念和技术要点。

    电机及其控制类.ppt

    以下是根据提供的信息提炼出的相关知识点: 1. **Java版本历史**: - Java起源于1991年的Oak项目,最初设计用于嵌入式设备。 - Java 1.1在1997年发布,引入了内部类和全新的事件处理模型。 - Java 1.2(1998年)...

    v3.0-JavaGuide面试突击版.pdf

    请看以下从标题、描述、标签和部分内容中提炼出来的详细知识点: ### Java基础知识 #### Java基本语法 - **变量、数据类型和运算符**:理解Java的基本数据类型(如int、char、float等),变量声明、赋值与使用...

    二十三种设计模式【PDF版】

    所以很少存在简单重复的工作,加上Java 代码的精炼性和面向对象纯洁性(设计模式是 java 的灵魂),编程工作将变成一个让你时刻 体验创造快感的激动人心的过程. 为能和大家能共同探讨"设计模式",我将自己在学习中的心得...

    FrameworkWithDesingPatterns:来自 Design Patterns and Architectures Book of Design Patterns Development 部分的代码

    首先,设计模式是解决常见软件问题的标准化解决方案,它们是从实践中提炼出的最佳实践。在《设计模式和架构》这本书中,作者详细阐述了多种设计模式,如工厂模式、单例模式、观察者模式、装饰器模式、代理模式等。...

Global site tag (gtag.js) - Google Analytics