JVM for JAVA basics - reflection and generics

Reprinted from: https://www.cnblogs.com/fysola/p/6112706.html

 

Generics and Class

Using generic Class<T> in reflection can avoid casts. The following is a simple example. If you don't use generics, you need to show conversions.

copy code
 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 }
copy code

Lines 19 and 20 of the above example both need to cast the Object result returned by newInstance of Class to the target type, which is not only troublesome, but more importantly, in some cases, errors cannot be found.

For example, in line 20, the coercion can be compiled, but an error may be reported during runtime . The reason is that the Date type is forcibly converted to the JFrame type, and the types do not match. The following is the execution result,

1 Exception in thread "main" java.lang.ClassCastException: java.util.Date incompatible with javax.swing.JFrame
2     at aop.ObjectFactory.main(ObjectFactory.java:20)

The following uses generics to implement getInstance, that is, public static <T> T getInstance(Class<T> cls)

copy code
 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 }
copy code

上面的第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包装成泛型之后再使用的例子,

copy code
 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 }
copy code

输出结果, 可以看到上面已经刻在无需强制转换成数组的情况下,直接将arr和intArr作为数组来使用了,

1 天王盖地虎
2 12

 使用反射来获取泛型信息

通过反射可以获取指定类的成员变量, 方法是 Field f = clazz.getDeclaredField("成员变量名称");

之后,可以用f对象获取它的类型,对于普通类型的成员变量,可以用Class<?> a = f.getType(); 方式获取

但是对于泛型的成员变量,例如 Map<String, Integer> score; getType()只能获取原始类型,这里是Map

如果要获取泛型类型(即尖括号里面的参数类型),

  1. 首先需要获取成员变量的泛型类型, 通过Type gType = f.getGenericType();
  2. 然后将泛型对象gType强制转换成参数化类型ParameterizedType的对象pType;  (ParameterizedType包含两个方法,getRawType(), 返回原始类型,和上面的getType一样。getActualTypeArguments(), 返回泛型参数的类型,也就是Map<String, Integer> 括号里的类型)
  3. 使用getActualTypeArguments()返回泛型信息

下面来演示一下使用反射来获取泛型信息,

copy code
 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("Error getting generic type" );
 34          }
 35      }
 36 }
copy code

output result,

1 The type of score is: interface java.util.Map
 2 The original type: interface java.util.Map
 3 The  generic parameter type:
 4 The generic type of the [0]th parameter is: class java.lang.String
 5 The [ 1] The generic type of the parameters is: class java.lang.Integer

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326097530&siteId=291194637