`
wesker0918
  • 浏览: 42102 次
  • 性别: Icon_minigender_1
  • 来自: 山东->北京
社区版块
存档分类
最新评论

泛型与反射

阅读更多

我们知道,反射和泛型都是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(StringclsName){
		try{
			Class<?> cls = Class.forName(clsName);
			return(U) cls.newInstance();
		}catch(Exceptione){
			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");这句,换成Cls2i1 = Factory.getInstance("fanxing.factory.dynaFactory.Cls1");也是可以编译过去的。只是在运行的时候才会出错。

除了上面的方法,还有一种更好的方法。

这个方法需要我们在运行期内传入的对象是Class对象,当然是经过泛型的Class对象,就像下面的样子:

Class<Cls1> cls = Cls1.class;
try{
	Intf1 obj = cls.newInstance();
	obj.do1();
}catch(Exceptione){
	e.printStackTrace();
}

 

可以很清楚地看到,这里完全没有了强制类型转化,当然也就没有第一种方法所出现的那两个问题。

运行结果为:

cls1...

根据这个方法,我们可以把前面的初始化方法改造为:

public static <U extendsObject> U getInstance(Class<U> cls){
	try{
		return cls.newInstance();
	}catch(Exceptione){
		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",newClass[0]);
	return m.invoke(this,new Object[0]);
}catch(Exceptione){
	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(Exceptione){
	e.printStackTrace();
	return null;
}

 

同样,我们的代码也会遇到警告性的错误。但是我们可以置之不理。

 

3. 运行期内初始化数组对象

同样,在运行期内初始化得到的数组也是一个Object对象。就像下面的样子:

Object o = Array.newInstance(int.class,10);

 

如果我想在getArray()方法里得到数组对象,就会得到像下面这个样子的代码:

public Object getArray(Classcls,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;
	}
	……
}

 

这当然是我们最基本的用法,但常常会这样:编译器希望知道更多的关于这个未知对象如Afst的信息,这样,我们可以在运行期内调用某一些方法。大家说啊,这很容易啊,我们可以把这种未知类型作为参数输入。呵呵,这就对了,有了这样参数,下一步,我们就要使用反射在运行期内调用它的方法。

关于这样的例子,我在这里不再给出。我在这里给出一个简单一些的例子,就是对泛型类初始化需要调用的构造器。

对于上面的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("rawTypeis instance of "+rType.getClass().getName());
		System.out.println("(" + rType + ")");
		Type[] tArgs = pType.getActualTypeArguments();
		System.out.println("actualtype 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("getGenericTypeis not a ParameterizedType!");
	}
}catch(Exception e){
	e.printStackTrace();
}

 

输出结果为:

rawType is instance ofjava.lang.Class
 (class Pair)
actual type arguments are:
 instance ofjava.lang.Class:
 (classjava.lang.String)
 instance ofjava.lang.Class:
 (interfacejava.util.List)

分享到:
评论

相关推荐

    基于泛型与反射的万能数据库操作代码

    本篇文章将详细讲解如何利用泛型与反射来创建一个万能的数据库操作代码,如同标题所示,这可以极大提高代码的复用性和可维护性。 **泛型(Generics)** 泛型是Java SE 5.0引入的一个新特性,它允许在编译时检查...

    java 基于泛型与反射的通用 DAO

    在Java编程语言中,泛型和反射是两个非常重要的特性,它们可以极大地提高代码的复用性和灵活性。本文将深入探讨如何结合这两种技术实现一个通用的DAO(Data Access Object)设计模式。 首先,我们来看“泛型”。...

    浅论泛型与反射机制结合(hibernate)

    本篇文章将深入探讨泛型与反射机制的基本概念,以及它们在Hibernate中的应用。 泛型是Java 5引入的一个重要特性,它允许在类、接口和方法中使用类型参数,以提供编译时的类型安全性和代码复用。泛型的主要优点包括...

    黑马程序员----泛型与反射的小运用

    在Java编程语言中,泛型和反射是两个非常重要的特性,它们在软件开发中有着广泛的应用。本篇文章将深入探讨这两个概念以及它们在实际开发中的小运用。 首先,我们来看泛型(Generics)。泛型是在Java SE 5.0引入的...

    j2ee泛型和反射有趣的集合

    将泛型与反射结合,可以缓解反射中的一些不便。正如示例所示,我们可以通过泛型方法来减少强制类型转换。在示例的`Factory.getInstance()`方法中,通过使用类型参数`&lt;U extends Object&gt;`,我们可以在方法内部完成...

    java泛型和反射机制

    对java泛型以及反射机制进行原理和应用上的讲解,帮助初学者对这两个概念进行更轻松的掌握

    泛型 反射 相关概念方法使用

    泛型和反射是Java编程语言中的两个重要特性,它们各自有着独特的功能,但在某些场景下也可以结合使用。本文将深入探讨这两个概念以及相关的使用方法。 首先,我们来了解泛型。泛型是Java 5引入的一项特性,它允许在...

    基于泛型和反射的三层架构雏形(另一个旧版不能删除)

    同时,因为反射会带来性能损失,因此,可根据自己需求,针对每个类型轻松在两种模式之前切换,本例源码,测试实例俱全,而且代码浅显易懂,只要对泛型、反射、三层架构有一定了解的人都能轻松学习

    泛型的反射分析代码可参考复杂未看懂

    然而,当泛型与反射结合时,事情变得稍微复杂。由于Java的泛型是类型擦除的,即在编译后的字节码中并不保留泛型信息。因此,通过反射获取到的Class对象或Method对象并不会直接包含泛型类型信息。但这并不意味着无法...

    JAVA5泛型和反射

    Java 5 引入了泛型和反射两个重要的特性,极大地增强了编程的灵活性和安全性。泛型主要是为了解决在编程中频繁进行类型转换的问题,而反射则允许程序在运行时动态地获取类的信息和调用方法。 泛型类型: 在 Java 5 ...

    C#泛型、反射实例、自动生成sql语句

    本篇文章将详细探讨C#中的泛型、反射以及如何利用这些特性来实现自动生成SQL语句。 **一、C# 泛型** 泛型是C#的一个核心特性,它允许我们编写类型参数化的代码,即代码可以在多种数据类型上工作,而无需进行显式的...

    基于泛型和反射的三层架构雏形

    鉴于使用三层架构的过程中,数据库变动造成大量代码改动的问题,特意对三层架构进行了改进,数据库变动只需要简单...本例源码,测试实例俱全,而且代码浅显易懂,只要对泛型、反射、三层架构有一定了解的人都能轻松学习

    利用反射生成泛型类对象

    反射和泛型是一种重要的解决途径。 此代码是一个生成泛型对象的类。 比如: Pool&lt;Point&gt; pool = new Pool(){}; Point p = pool.get(x, y); //在此构造Point对象 ... pool.put(p); 希望能帮助那些为查找泛型构造器、...

    泛型+反射:泛型 笔记 ,课后作业

    泛型+反射:泛型 笔记 ,课后作业

    Java泛型和反射机制

    初步学习Java的泛型和反射机制,通过一些简单的例子来学习泛型,反射

    java反射全解(反射原理+反射API详解+反射与数组+反射与泛型+反射源码与性能开销+反射优缺点+反射与内省)

    反射机制的分类包括反射 API、反射与数组、反射与泛型、反射源码与性能开销、反射优缺点、反射与内省等。 反射 API 是 Java 语言提供的一组 Application Programming Interface(API),用于在运行时获取类的信息和...

    使用泛型和反射,打造我们的完美实体基类

    2. **动态序列化与反序列化**:我们可以利用反射将实体对象转化为JSON字符串,或将JSON字符串还原为实体对象。这在数据交换或持久化存储时非常有用。 3. **类型安全的比较**:通过泛型,我们可以确保比较的两个实体...

    基于泛型反射的数据层封装+MSSQLJDBC3.0驱动

    在这个特定的案例中,"基于泛型反射的数据层封装"是利用了Java的泛型和反射特性来实现这一目标。泛型提供了一种在编译时类型安全的方式,允许我们创建可以处理多种类型的类、接口和方法。而反射则是Java提供的一种...

Global site tag (gtag.js) - Google Analytics