泛型和Class类
在反射中使用泛型Class<T>可以避免强制类型转换,下面是一个简单例子,如果不使用泛型的话,需要显示转换,
1 package aop; 2 3 import java.util.Date; 4 5 import javax.swing.JFrame; 6 7 public class ObjectFactory { 8 public static Object getInstance(String clsName) { 9 try { 10 Class cls = Class.forName(clsName); 11 return cls.newInstance(); 12 } catch (Exception e) { 13 e.printStackTrace(); 14 return null; 15 } 16 } 17 18 public static void main(String[] args) { 19 Date d = (Date)ObjectFactory.getInstance("java.util.Date"); 20 JFrame f = (JFrame)ObjectFactory.getInstance("java.util.Date"); 21 } 22 }
上面的例子第19行和20行都需要将Class的newInstance返回的Object结果强制转换成目标类型,这不仅麻烦,更重要的是有些情况下无法发现错误,
例如第20行,强制转换可以通过编译的,但是运行期间可能会报错,原因是强制将Date类型转换成JFrame类型,类型不匹配,下面是执行结果,
1 Exception in thread "main" java.lang.ClassCastException: java.util.Date incompatible with javax.swing.JFrame
2 at aop.ObjectFactory.main(ObjectFactory.java:20)
下面用泛型来实现getInstance, 即 public static <T> T getInstance(Class<T> cls)
1 package aop; 2 3 import java.util.Date; 4 5 import javax.swing.JFrame; 6 7 public class ObjectFactory2 { 8 public static <T> T getInstance(Class<T> cls) { 9 try { 10 return cls.newInstance(); 11 } catch (Exception e) { 12 e.printStackTrace(); 13 return null; 14 } 15 } 16 17 public static void main(String[] args) { 18 Date d = ObjectFactory2.getInstance(Date.class); 19 JFrame f = ObjectFactory2.getInstance(JFrame.class); 20 } 21 }
上面的第19行如果类型不符,例如写成了JFrame f = ObjectFactory2.getInstance(Date.class); , 则无法通过编译,
必须要修改成匹配的类型才能通过编译, 很好地编码了隐形错误。
泛型和数组
泛型中的数组虽然也使用了泛型编程,但却没有实现泛型功能, 泛型数组的newInstance方法签名是这样的,
1 public static Object newInstance(Class<?> componentType, int... dimensions)
虽然上面的定义也使用了泛型,但是却没有真正实现泛型,所以这个方法返回的对象arr依然是个Object类型,要当成数组使用的话还是需要强制转换成数组类型。
如果将上面的方法签名改成这样,
1 public static <T> T[] newInstance(Class<T> componentType, int length)
这样返回的arr就是个数组类型了,无需强制转换就可以当作数组用。
下面是一个将反射(reflect)中的Array的newInstance包装成泛型之后再使用的例子,
1 package aop; 2 3 import java.lang.reflect.Array; 4 5 public class MyArray { 6 public static <T> T[] newInstance(Class<T> componentType, int length) { 7 return (T[])Array.newInstance(componentType, length); 8 } 9 10 public static void main(String[] args) { 11 //使用MyArry的newInstance创建数组 12 String[] arr = MyArray.newInstance(String.class, 10); 13 //使用MyArray的newInstance创建二维数组 14 int[][] intArr = MyArray.newInstance(int[].class, 5); 15 arr[5] = "天王盖地虎"; 16 intArr[1] = new int[]{23,12}; 17 System.out.println(arr[5]); 18 System.out.println(intArr[1][1]); 19 } 20 }
输出结果, 可以看到上面已经刻在无需强制转换成数组的情况下,直接将arr和intArr作为数组来使用了,
1 天王盖地虎 2 12
使用反射来获取泛型信息
通过反射可以获取指定类的成员变量, 方法是 Field f = clazz.getDeclaredField("成员变量名称");
之后,可以用f对象获取它的类型,对于普通类型的成员变量,可以用Class<?> a = f.getType(); 方式获取
但是对于泛型的成员变量,例如 Map<String, Integer> score; getType()只能获取原始类型,这里是Map
如果要获取泛型类型(即尖括号里面的参数类型),
- 首先需要获取成员变量的泛型类型, 通过Type gType = f.getGenericType();
- 然后将泛型对象gType强制转换成参数化类型ParameterizedType的对象pType; (ParameterizedType包含两个方法,getRawType(), 返回原始类型,和上面的getType一样。getActualTypeArguments(), 返回泛型参数的类型,也就是Map<String, Integer> 括号里的类型)
- 使用getActualTypeArguments()返回泛型信息
下面来演示一下使用反射来获取泛型信息,
1 package aop; 2 3 4 import java.lang.reflect.Field; 5 import java.lang.reflect.ParameterizedType; 6 import java.lang.reflect.Type; 7 import java.util.Map; 8 9 public class GenericTest { 10 private Map<String, Integer> score; 11 public static void main(String[] args) throws Exception { 12 Class<GenericTest> clazz = GenericTest.class; 13 Field f = clazz.getDeclaredField("score"); 14 //getType只能取出普通类型,因此下面的a只能得到java.util.Map类型 15 Class<?> a = f.getType(); 16 System.out.println("score的类型是: " + a); 17 //获取泛型类型 18 Type gType = f.getGenericType(); 19 // 如果gType类型是ParameterizedType对象 20 if (gType instanceof ParameterizedType) { 21 //泛型对象强制转换成参数化类型对象 22 ParameterizedType pType = (ParameterizedType)gType; 23 //获取原始类型,即java.util.Map 24 Type rType = pType.getRawType(); 25 System.out.println("原始类型: "+rType); 26 //获取泛型括号里面参数的类型 27 System.out.println("泛型参数类型:"); 28 Type[] tArgs = pType.getActualTypeArguments(); 29 for (int i = 0; i < tArgs.length; i++ ) { 30 System.out.println("第["+i+"]个参数的泛型类型是: "+tArgs[i]); 31 } 32 } else { 33 System.out.println("获取泛型类型错误"); 34 } 35 } 36 }
输出结果,
1 score的类型是: interface java.util.Map 2 原始类型: interface java.util.Map 3 泛型参数类型: 4 第[0]个参数的泛型类型是: class java.lang.String 5 第[1]个参数的泛型类型是: class java.lang.Integer