转载:http://www.gd-emb.org/detail/id-33870.html
摘要:反射和泛型的功能都十分强大,将它们结合起来也特别有趣,本文试图就Java语言的反射和泛型的关系做一点探讨,用以抛砖引玉,引起大家对两种技术的兴趣!
研究泛型与反射之间的关系非常有趣。
我们知道,反射和泛型都是Java的一种动态技术。而不像继承和多态,是面向对象的技术。可以这么说,反射和泛型都像是为了弥补像继承和多态这些面向对象技术的不足而产生的。模式多是建立在面向对象技术基础上的,因而每种模式多多少少在动态性,或者说扩展性方面有些不足,我们就又结合了反射和泛型对模式进行一定的扩展,使它在动态性方面更符合我们的要求。
在将这些技术结合起来的过程中,我们多多少少会想到将泛型和反射结合起来。这是非常有趣的话题:范型和反射都使得Java有了一定的扩展性,但同时都有它自己的不足,而将这两种技术结合起来,是不是又能解决各自的不足,使得它们在动态性上更上一层楼呢?
正像前面所说的,泛型和反射可以相互促进,我们先来看看泛型是怎么帮助反射的。
我们知道,使用反射的一个最大的烦恼就是应用反射以后得到的基本上都是Object类型的对象,这种对象要使用,需要我们进行强制类型转化。而我们更知道,泛型的功能之一就是消除我们使用强制类型转化。
1. 运行期内初始化对象
运行期内初始化对象是我们最常用的反射功能,但是我们通过反射在运行期内得到的对象的类型通常是Object类型的,而这个对象又需要我们在使用的时候进行强制类型转化。现在,有了反射,可以使我们不需要做强制类型转化这个工作。
假设我们已经有了两个类:
我们需要在运行期内初始化这两个对象,我们可以设计如下的初始化方法:
在这个方法里,我们其实是利用泛型在初始化方法里提前做了强制类型转化的工作,这样使得我们不必在使用的时候进行强制类型转化的工作。
它们的测试代码如下:
测试结果为:
cls1...
cls2...
需要注意的是,使用这种方法有几个问题:
第一, return (U) cls.newInstance();这个语句会有警告性的错误,好在这种错误不是编译性的错误,还是我们可以承受的。
第二, 编译器不能做类型检查,如Cls1 i1 = Factory.getInstance("fanxing.factory.dynaFactory.Cls1");这句,换成Cls2 i1 = Factory.getInstance("fanxing.factory.dynaFactory.Cls1");也是可以编译过去的。只是在运行的时候才会出错。
除了上面的方法,还有一种更好的方法。
这个方法需要我们在运行期内传入的对象是Class对象,当然是经过泛型的Class对象,就像下面的样子:
可以很清楚地看到,这里完全没有了强制类型转化,当然也就没有第一种方法所出现的那两个问题。
运行结果为:
cls1...
根据这个方法,我们可以把前面的初始化方法改造为:
我们来进行以下测试:
Cls1 c1 = Factory.getInstance(Cls1.class);
c1.do1();
测试结果为:
cls1...
这时候,如果我们将上面的测试代码改为:
Cls2 c1 = Factory.getInstance(Cls1.class);
就会出现编译错误,可以看到,我们的第二种方法的确避免了第一种方法的弱点,但第一种方法的好处是,只需要往初始化方法里输入类名作为参数。
2. 运行期内调用方法
使用过反射的人都知道,运行期内调用方法后得到的结果也将是一个Object对象。这样的结果在一般的方法反射上也就是可以忍受的了。但是有时候也是不能忍受的,比如,我们想反射List<T>类的iterator()方法。
一般的,我们使用反射可以这么做:
当然,这里返回的是一个Object对象,而List<T>类的iterator()的实际返回类型为T。很明显,我们可以将上面的代码做如下的修改:
同样,我们的代码也会遇到警告性的错误。但是我们可以置之不理。
3. 运行期内初始化数组对象
同样,在运行期内初始化得到的数组也是一个Object对象。就像下面的样子:
Object o = Array.newInstance(int.class,10);
如果我想在getArray()方法里得到数组对象,就会得到像下面这个样子的代码:
这显然不会令我们满意。因为如果我们输入一个Class<T>类型的参数,就希望返回一个T类型的结果。
在这种想法下,我们就使用泛型将getArray()方法做如下修改:
这样的修改,将使我们得到一个比较满意的结果。同样,上面的代码也会得到一个警告性的错误。但是,使用泛型使我们的结果得到了很大的优化。
上面的几个例子让我们看到了泛型能够帮助反射,而且是大大优化了反射的结果。但在同时,反射也不是被动的接受泛型的帮助,反射同样也可以帮助泛型。这是基于反射的基本工作原理:得到数据的元数据。也就是说,可以通过反射得到泛型的元数据。除此之外,反射也有利于帮助泛型数据运行期内初始化。
下面以一两个简单例子加以说明。
4. 使用反射初始化泛型类
我们通常会在一个类里这样使用泛型:
这当然是我们最基本的用法,但常常会这样:编译器希望知道更多的关于这个未知对象如A fst的信息,这样,我们可以在运行期内调用某一些方法。大家说啊,这很容易啊,我们可以把这种未知类型作为参数输入。呵呵,这就对了,有了这样参数,下一步,我们就要使用反射在运行期内调用它的方法。
关于这样的例子,我在这里不再给出。我在这里给出一个简单一些的例子,就是对泛型类初始化需要调用的构造器。
对于上面的Pair<A,B>类,如果构造器的输入参数的类型不是A和B,而是Class<A>和Class<B>,那么我们就不得不在构造器里使用反射了。
由此可见,对于泛型里的未知类型参数,我们也完全可以和普通类型一样使用反射工具。即可以通过反射对这些未知类型参数做反射所能做到的任何事情。
5. 使用反射得到泛型信息
关于这一个小结的问题,显得更加得有趣。
我们还是以上面的Pair<A,B>作为例子,假如我们在一个类中使用到了这个类,如下所示:
如果我们对PairUser类应用反射,我们可以很轻松的得到该类的属性pair的一些信息,如它是private还是public的,它的类型等等。
如果我们通过反射得到pair属性的类型为Pair以后,我们知道该类是一个泛型类,那么我们就想进一步知道该泛型类的更进一步的信息。比如,泛型类的类名,泛型参数的信息等等。
具体到上面的PairUser类的例子,我现在想知道它的属性pair的一些信息,比如,它的类型名、泛型参数的类型,如String和List等等这些信息。所要做的工作如下:
首先是取得这个属性
Field field = PairUser.class.getDeclaredField("pair");
然后是取得属性的泛型类型
Type gType = field.getGenericType();
再判断gType是否为ParameterizedType类型,如果是则转化为ParameterizedType类型的变量
ParameterizedType pType = (ParameterizedType)gType;
取得原始类型
Type rType = pType.getRawType();
然后就可以通过rType.getClass().getName()获得属性pair的类型名。
最后获取参数信息
Type[] tArgs = pType.getActualTypeArguments();
可以通过tArgs[j].getClass().getName()取得属性pair的泛型参数的类型名。
完整的代码如下:
输出结果为:
rawType is instance of java.lang.Class
(class Pair)
actual type arguments are:
instance of java.lang.Class:
(class java.lang.String)
instance of java.lang.Class:
(interface java.util.List)
摘要:反射和泛型的功能都十分强大,将它们结合起来也特别有趣,本文试图就Java语言的反射和泛型的关系做一点探讨,用以抛砖引玉,引起大家对两种技术的兴趣!
研究泛型与反射之间的关系非常有趣。
我们知道,反射和泛型都是Java的一种动态技术。而不像继承和多态,是面向对象的技术。可以这么说,反射和泛型都像是为了弥补像继承和多态这些面向对象技术的不足而产生的。模式多是建立在面向对象技术基础上的,因而每种模式多多少少在动态性,或者说扩展性方面有些不足,我们就又结合了反射和泛型对模式进行一定的扩展,使它在动态性方面更符合我们的要求。
在将这些技术结合起来的过程中,我们多多少少会想到将泛型和反射结合起来。这是非常有趣的话题:范型和反射都使得Java有了一定的扩展性,但同时都有它自己的不足,而将这两种技术结合起来,是不是又能解决各自的不足,使得它们在动态性上更上一层楼呢?
正像前面所说的,泛型和反射可以相互促进,我们先来看看泛型是怎么帮助反射的。
我们知道,使用反射的一个最大的烦恼就是应用反射以后得到的基本上都是Object类型的对象,这种对象要使用,需要我们进行强制类型转化。而我们更知道,泛型的功能之一就是消除我们使用强制类型转化。
1. 运行期内初始化对象
运行期内初始化对象是我们最常用的反射功能,但是我们通过反射在运行期内得到的对象的类型通常是Object类型的,而这个对象又需要我们在使用的时候进行强制类型转化。现在,有了反射,可以使我们不需要做强制类型转化这个工作。
假设我们已经有了两个类:
public class Cls1 { public void do1() { // TODO Auto-generated method stub System.out.println("cls1..."); } } public class Cls2 { public void do2() { // TODO Auto-generated method stub System.out.println("cls2..."); } }
我们需要在运行期内初始化这两个对象,我们可以设计如下的初始化方法:
public class Factory{ public static <U extends Object>U getInstance(String clsName) { try { Class<?> cls = Class.forName(clsName); return (U) cls.newInstance(); } catch(Exception e) { e.printStackTrace(); return null; } } }
在这个方法里,我们其实是利用泛型在初始化方法里提前做了强制类型转化的工作,这样使得我们不必在使用的时候进行强制类型转化的工作。
它们的测试代码如下:
Cls1 i1 = Factory.getInstance("fanxing.factory.dynaFactory.Cls1"); i1.do1(); Cls2 i2 = Factory.getInstance("fanxing.factory.dynaFactory.Cls2"); i2.do2()
测试结果为:
cls1...
cls2...
需要注意的是,使用这种方法有几个问题:
第一, return (U) cls.newInstance();这个语句会有警告性的错误,好在这种错误不是编译性的错误,还是我们可以承受的。
第二, 编译器不能做类型检查,如Cls1 i1 = Factory.getInstance("fanxing.factory.dynaFactory.Cls1");这句,换成Cls2 i1 = Factory.getInstance("fanxing.factory.dynaFactory.Cls1");也是可以编译过去的。只是在运行的时候才会出错。
除了上面的方法,还有一种更好的方法。
这个方法需要我们在运行期内传入的对象是Class对象,当然是经过泛型的Class对象,就像下面的样子:
Class<Cls1> cls = Cls1.class; try { Intf1 obj = cls.newInstance(); obj.do1(); } catch(Exception e) { e.printStackTrace(); }
可以很清楚地看到,这里完全没有了强制类型转化,当然也就没有第一种方法所出现的那两个问题。
运行结果为:
cls1...
根据这个方法,我们可以把前面的初始化方法改造为:
public static <U extends Object> U getInstance(Class<U> cls) { try { return cls.newInstance(); } catch(Exception e) { e.printStackTrace(); return null; } }
我们来进行以下测试:
Cls1 c1 = Factory.getInstance(Cls1.class);
c1.do1();
测试结果为:
cls1...
这时候,如果我们将上面的测试代码改为:
Cls2 c1 = Factory.getInstance(Cls1.class);
就会出现编译错误,可以看到,我们的第二种方法的确避免了第一种方法的弱点,但第一种方法的好处是,只需要往初始化方法里输入类名作为参数。
2. 运行期内调用方法
使用过反射的人都知道,运行期内调用方法后得到的结果也将是一个Object对象。这样的结果在一般的方法反射上也就是可以忍受的了。但是有时候也是不能忍受的,比如,我们想反射List<T>类的iterator()方法。
一般的,我们使用反射可以这么做:
try { Class<?> cls = Class.forName("java.util.ArrayList"); Method m = cls.getDeclaredMethod("iterator",new Class[0]); return m.invoke(this,new Object[0]); } catch(Exception e) { e.printStackTrace(); return null; }
当然,这里返回的是一个Object对象,而List<T>类的iterator()的实际返回类型为T。很明显,我们可以将上面的代码做如下的修改:
try { Class<?> cls = Class.forName("java.util.ArrayList"); Method m = cls.getDeclaredMethod("iterator",new Class[0]); return (T)m.invoke(this,new Object[0]); } catch(Exception e) { e.printStackTrace(); return null; }
同样,我们的代码也会遇到警告性的错误。但是我们可以置之不理。
3. 运行期内初始化数组对象
同样,在运行期内初始化得到的数组也是一个Object对象。就像下面的样子:
Object o = Array.newInstance(int.class,10);
如果我想在getArray()方法里得到数组对象,就会得到像下面这个样子的代码:
public Object getArray(Class cls,int size) { return Array.newInstance(cls,size); }
这显然不会令我们满意。因为如果我们输入一个Class<T>类型的参数,就希望返回一个T类型的结果。
在这种想法下,我们就使用泛型将getArray()方法做如下修改:
public T[] getArray(Class<T> cls,int size) { return (T[])Array.newInstance(cls,size); }
这样的修改,将使我们得到一个比较满意的结果。同样,上面的代码也会得到一个警告性的错误。但是,使用泛型使我们的结果得到了很大的优化。
上面的几个例子让我们看到了泛型能够帮助反射,而且是大大优化了反射的结果。但在同时,反射也不是被动的接受泛型的帮助,反射同样也可以帮助泛型。这是基于反射的基本工作原理:得到数据的元数据。也就是说,可以通过反射得到泛型的元数据。除此之外,反射也有利于帮助泛型数据运行期内初始化。
下面以一两个简单例子加以说明。
4. 使用反射初始化泛型类
我们通常会在一个类里这样使用泛型:
public final class Pair<A,B> { public final A fst; public final B snd; public Pair(A fst, B snd) { this.fst = fst; this.snd = snd; } …… }
这当然是我们最基本的用法,但常常会这样:编译器希望知道更多的关于这个未知对象如A fst的信息,这样,我们可以在运行期内调用某一些方法。大家说啊,这很容易啊,我们可以把这种未知类型作为参数输入。呵呵,这就对了,有了这样参数,下一步,我们就要使用反射在运行期内调用它的方法。
关于这样的例子,我在这里不再给出。我在这里给出一个简单一些的例子,就是对泛型类初始化需要调用的构造器。
对于上面的Pair<A,B>类,如果构造器的输入参数的类型不是A和B,而是Class<A>和Class<B>,那么我们就不得不在构造器里使用反射了。
public Pair(Class<A> typeA, Class<B> typeB) { this.fst = typeA.newInstance(); this.snd = typeB.newInstance(); …… }
由此可见,对于泛型里的未知类型参数,我们也完全可以和普通类型一样使用反射工具。即可以通过反射对这些未知类型参数做反射所能做到的任何事情。
5. 使用反射得到泛型信息
关于这一个小结的问题,显得更加得有趣。
我们还是以上面的Pair<A,B>作为例子,假如我们在一个类中使用到了这个类,如下所示:
public Class PairUser { private Pair<String,List> pair; …… }
如果我们对PairUser类应用反射,我们可以很轻松的得到该类的属性pair的一些信息,如它是private还是public的,它的类型等等。
如果我们通过反射得到pair属性的类型为Pair以后,我们知道该类是一个泛型类,那么我们就想进一步知道该泛型类的更进一步的信息。比如,泛型类的类名,泛型参数的信息等等。
具体到上面的PairUser类的例子,我现在想知道它的属性pair的一些信息,比如,它的类型名、泛型参数的类型,如String和List等等这些信息。所要做的工作如下:
首先是取得这个属性
Field field = PairUser.class.getDeclaredField("pair");
然后是取得属性的泛型类型
Type gType = field.getGenericType();
再判断gType是否为ParameterizedType类型,如果是则转化为ParameterizedType类型的变量
ParameterizedType pType = (ParameterizedType)gType;
取得原始类型
Type rType = pType.getRawType();
然后就可以通过rType.getClass().getName()获得属性pair的类型名。
最后获取参数信息
Type[] tArgs = pType.getActualTypeArguments();
可以通过tArgs[j].getClass().getName()取得属性pair的泛型参数的类型名。
完整的代码如下:
try { Field field = PairUser.class.getDeclaredField("pair"); Type gType = field.getGenericType(); if(gType instanceof ParameterizedType) { ParameterizedType pType = (ParameterizedType)gType; Type rType = pType.getRawType(); System.out.println("rawType is instance of " + rType.getClass().getName()); System.out.println(" (" + rType + ")"); Type[] tArgs = pType.getActualTypeArguments(); System.out.println("actual type arguments are:"); for (int j = 0; j < tArgs.length; j++) { System.out.println(" instance of " + tArgs[j].getClass().getName() + ":"); System.out.println(" (" + tArgs[j] + ")"); } } else { System.out.println("getGenericType is not a ParameterizedType!"); } } catch(Exception e) { e.printStackTrace(); } }
输出结果为:
rawType is instance of java.lang.Class
(class Pair)
actual type arguments are:
instance of java.lang.Class:
(class java.lang.String)
instance of java.lang.Class:
(interface java.util.List)
发表评论
-
动态ClassLoader
2013-03-21 22:29 807代码来源 http://www.oschina.net/co ... -
JAVA TIMER用法学习
2011-12-29 18:53 1065JAVA TIMER用法学习 引用http://www.blo ... -
svn版本文件删除
2010-04-08 04:23 832转自:http://www.subversion.org.cn ... -
java运算符
2009-12-30 11:22 1130【转自】http://blog.csdn.net/lengch ... -
jre 环境配置
2009-11-25 15:08 5359CLASSPATH=.;D:\program files\jd ... -
LDAP一些网站资料
2009-11-14 12:21 1203LDAP技术资源 转自http://blog.csdn.net ... -
sturts2中sturts.xml中result中的 type 含义
2009-10-09 17:12 1674转自:http://tieba.baidu.com/f?kz= ... -
配置支持SSL的Tomcat
2009-09-29 11:51 1139转自http://www.wangchao.net.c ... -
struts2中的常用标签
2009-09-21 19:38 8241. Struts2页面开发中常用标签使用说明 1.1.往a ... -
java.lang.UnsupportedClassVersionError: Bad version number in .class file异常
2009-09-19 20:40 1977转自http://hi.baidu.com/haihe118/ ... -
JDBC连接__数据库
2009-08-24 12:04 8221.Access Class.forName(" ... -
org.jboss.xb.binding.JBossXBRuntimeException
2009-08-21 10:39 3223org.jboss.xb.binding.JBossXBR ...
相关推荐
在"反射与泛型.pptx"这个文件中,可能包含了对这些概念的详细讲解、示例代码以及实际应用案例,建议进一步查阅以获取更全面的知识。记住,理论知识的掌握只是第一步,实践和应用才能真正提升编程技能。
Java反射与泛型是Java编程中的两个重要特性,它们各自为开发者提供了强大的工具来增强代码的灵活性和类型安全性。在本教程中,我们将深入探讨这两个主题,帮助你更好地理解和运用它们。 **Java反射** Java反射机制...
反射机制的分类包括反射 API、反射与数组、反射与泛型、反射源码与性能开销、反射优缺点、反射与内省等。 反射 API 是 Java 语言提供的一组 Application Programming Interface(API),用于在运行时获取类的信息和...
Java反射和泛型是Java编程中的两个重要特性,它们各自为开发者提供了强大的功能,并且在特定情况下可以相互结合使用。本文将深入探讨这两个概念,并通过一个具体的`Testrefl.java`示例来阐述它们的应用。 Java反射...
反射和泛型是一种重要的解决途径。 此代码是一个生成泛型对象的类。 比如: Pool<Point> pool = new Pool(){}; Point p = pool.get(x, y); //在此构造Point对象 ... pool.put(p); 希望能帮助那些为查找泛型构造器、...
Java 5 引入了泛型和反射两个重要的特性,极大地增强了编程的灵活性和安全性。泛型主要是为了解决在编程中频繁进行类型转换的问题,而反射则允许程序在运行时动态地获取类的信息和调用方法。 泛型类型: 在 Java 5 ...
对java泛型以及反射机制进行原理和应用上的讲解,帮助初学者对这两个概念进行更轻松的掌握
Java试题-3:反射和泛型的综合应用 Java反射 泛型都是比较高级的应用技术
在C#中通过反射操作一个List泛型。。
通过反射创建带有泛型参数的类 1.Limin.Reflector.DLL中的BaseDal.cs里包含要创建的带泛型参数的类 2.Limin.Reflector.Invoke中的Factory.cs完成泛型类的创建 代码写的不是很好,不足之处,请多多指教
本资源"反射泛型完美版分页.rar"似乎提供了一个结合了这两种特性的分页解决方案,特别适用于处理大量数据的情况。下面我们将详细探讨反射、泛型以及它们如何应用于分页。 首先,让我们理解什么是反射。反射是Java的...
反射操作注解与反射操作泛型 在 Java 编程中,反射操作和泛型是两个重要的概念。反射操作是指在运行时检查和修改类、对象、方法、字段等信息的能力,而泛型是指在编译时检查和限制类型的能力。本文将详细介绍反射...
Java编程语言中的反射和泛型是两个非常重要的概念,它们为开发者提供了强大的工具来操作类、接口、对象以及参数类型。在"myreflect.rar"这个压缩包中,我们可以期待找到一系列关于这两个主题的源代码示例,这将有助...
在Java编程语言中,泛型和反射是两个非常重要的特性,它们可以极大地提高代码的复用性和灵活性。本文将深入探讨如何结合这两种技术实现一个通用的DAO(Data Access Object)设计模式。 首先,我们来看“泛型”。...
通过反射与泛型相结合,开发者可以动态地操作带有泛型的类和对象,这对于框架设计和复杂业务逻辑尤为有用。 首先,我们要理解泛型的基本概念。泛型是在定义类、接口或方法时使用的类型参数,用尖括号 `<T>` 表示。`...
泛型+反射:泛型 笔记 ,课后作业
5. **反射与反射代理**:Java的`Proxy`类结合反射可以创建动态代理,这对于实现AOP(面向切面编程)或其他需要动态行为的场景非常有用。即使接口定义了泛型方法,我们也可以通过反射代理正确地调用它们。 总之,...
反射+泛型+三层 ^-^^-^^-^^-^^-^^-^^-^^-^^-^^-^^-^^-^^-^^-^^-^^-^^-^^-^^-^^-^^-^^-^^-^^-^^-^^-^^-^^-^^-^^-^^-^^-^^-^
泛型和反射是Java编程语言中的两个重要特性,它们各自有着独特的功能,但在某些场景下也可以结合使用。本文将深入探讨这两个概念以及相关的使用方法。 首先,我们来了解泛型。泛型是Java 5引入的一项特性,它允许在...