- 浏览: 122981 次
- 性别:
- 来自: 深圳
最新评论
-
陌路人丁:
给力,爱你~
提炼Java Reflection -
szjszj:
pType = (ParameterizedType)Bank ...
提炼Java Reflection -
tianhandigeng:
写得挺好的
提炼Java Reflection -
fanny52o:
...
对Spring JpaTransactionManager的误解 -
老竹枝:
为什么不直接写成下面这样?还省了一个子查询
select my ...
小记:使用Oracle rownum分页
反射是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:附件是一个较为全面的测试代码
- summary-reflection.rar (15.6 KB)
- 描述: 测试代码
- 下载次数: 182
评论
GenericArrayType gType = (GenericArrayType)pType.getActualTypeArguments()[0];
这里报错楼主java.lang.Class cannot be cast to java.lang.reflect.GenericArrayType
发表评论
-
领域模型驱动开发利器-Sculptor介绍
2013-01-29 16:43 2781最近花了一点时间了解Sculptor-一款面向领域模型开发利 ... -
对Spring JpaTransactionManager的误解
2012-07-04 22:56 6684在以前项目中验证Hibernate实现JPA规范的时候发现虽然 ... -
设置参数解决内存溢出
2008-07-15 22:02 1444在Java应用开发过程中碰到的内存溢出一般就是两类: java ... -
Struts2 对比 Struts
2008-06-24 00:19 2152最近看到一篇好文引用过来,作者sojava,原文地址http: ... -
Debugging with the Maven Jetty Plugin in Eclipse
2008-04-16 20:55 1541http://docs.codehaus.org/displa ... -
Spring JDBC 接口学习
2007-06-01 13:33 2821org.springframework.jdbc.core.P ... -
EJB3.0学习之路 让第一个Stateless Session Bean跑起来
2007-04-24 14:49 13153随着Spring的兴起,EJB2.0被彻底击败,但是EJB3. ... -
设计使用MultiActionController心得
2007-04-23 10:18 8534今天总结一下在一个Spring的项目中使用MultiActio ... -
切身感受Spring AOP的魅力
2007-04-09 19:00 11724今天说一下昨天的感受,事情是这样的: 在项目即将验收钱,架构师 ... -
拷贝对象要细心
2007-01-11 09:34 1765在说对象的拷贝问题之前,先提一下java对参数的传递。在Jav ... -
小记:使用Oracle rownum分页
2007-01-10 11:40 5057在oracle数据库中查询结果的行号使用伪列ROWNUM表示( ... -
总结学习Oracle Secure Enterprise Search
2007-01-08 20:51 3886学习Oracle SES一段时间了 ...
相关推荐
根据给定文件的信息,我们可以提炼出以下详细的Java学习目标及相关知识点: ### 1. 面向对象设计与分析(OOA/OOD)及模式 深入理解并掌握面向对象的设计原则,包括GOF(Gamma、Helm、Johnson、Vlissides)设计模式...
以下是根据标题和描述提炼出的Java语言核心知识点: 1. **Java简介**:Java是由Sun Microsystems(现已被Oracle收购)于1995年推出的面向对象的编程语言,它的设计目标是“一次编写,到处运行”(Write Once, Run ...
以下是从“JAVA常用英文”这一主题中提炼出的详细知识点,旨在帮助理解和运用这些基础且重要的术语。 ### abstract 抽象类或方法,用于定义一种模板,允许子类继承并实现其功能。在Java中,`abstract`关键字不能...
以下是根据标题和描述提炼出的一些关键知识点: 1. **代码风格与格式**:良好的编程习惯始于整洁的代码,包括合理的缩进、空格使用、注释清晰等。Java有标准的编码规范,如Oracle的Java Code Conventions,遵循这些...
Java是世界上最流行的编程语言之一,尤其在企业级应用开发中占据主导地位。...以上就是从文档标题和描述中提炼出的Java基础知识点,它们构成了学习Java的基础,并为更高级的开发奠定了坚实的基础。
11. **反射(Reflection)**:Java的反射机制允许在运行时检查类的信息并动态调用方法,这在元编程和框架开发中非常有用。 12. **异常处理(Exception Handling)**:学习如何捕获和处理程序运行时可能出现的错误,...
### Java开发与应用知识点详解 ...以上是根据给定文件中的标题、描述、标签以及部分内容提炼和扩展出的相关知识点。希望这些内容能够帮助您更好地理解和掌握Java开发与应用的核心概念和技术要点。
以下是根据提供的信息提炼出的相关知识点: 1. **Java版本历史**: - Java起源于1991年的Oak项目,最初设计用于嵌入式设备。 - Java 1.1在1997年发布,引入了内部类和全新的事件处理模型。 - Java 1.2(1998年)...
请看以下从标题、描述、标签和部分内容中提炼出来的详细知识点: ### Java基础知识 #### Java基本语法 - **变量、数据类型和运算符**:理解Java的基本数据类型(如int、char、float等),变量声明、赋值与使用...
所以很少存在简单重复的工作,加上Java 代码的精炼性和面向对象纯洁性(设计模式是 java 的灵魂),编程工作将变成一个让你时刻 体验创造快感的激动人心的过程. 为能和大家能共同探讨"设计模式",我将自己在学习中的心得...
首先,设计模式是解决常见软件问题的标准化解决方案,它们是从实践中提炼出的最佳实践。在《设计模式和架构》这本书中,作者详细阐述了多种设计模式,如工厂模式、单例模式、观察者模式、装饰器模式、代理模式等。...