泛型和Class类
使用Class泛型可以避免强制类型转换,如下代码是一个简单的对象工厂,该对象工厂可以根据指定类来提供该类的实例
public class CrazyitObjectFactory
{
public static Object getInstance(String clsName)
{
try
{
// 创建指定类对应的Class对象
Class cls = Class.forName(clsName);
// 返回使用该Class对象所创建的实例
return cls.getConstructor().newInstance();
}
catch (Exception e)
{
e.printStackTrace();
return null;
}
}
}
当使用这个类创建对象的时候,会是
// 获取实例后需要强制类型转换
Date d = (Date)Crazyit.getInstance("java.util.Date");
JFrame f = (JFrame)Crazyit.getInstance("java.util.Date");
然而在编译时不会有任何问题,但运行时会抛出ClassCastException,因为程序试图讲一个Date对象转换成JFrame对象,优化代码改写成使用泛型后的Class
import java.util.*;
import javax.swing.*;
public class CrazyitObjectFactory2
{
public static <T> T getInstance(Class<T> cls)
{
try
{
return cls.getConstructor().newInstance();
}
catch (Exception e)
{
e.printStackTrace();
return null;
}
}
public static void main(String[] args)
{
// 获取实例后无须类型转换
Date d = CrazyitObjectFactory2.getInstance(Date.class);
JFrame f = CrazyitObjectFactory2.getInstance(JFrame.class);
}
}
程序中getInstance()方法传入一个Class参数,这是一个泛型化的Class对象,调用该Class对象的newInstance()方法将返回一个T对象,在使用这个工厂类的getInstance()方法来产生对象时,无需使用强制类型转换,不会出现ClassCastException运行时异常
泛型和数组
// 使用Array的newInstance方法来创建一个数组
Object array1 = Array.newInstance(String.class, 10);
代码中newInstance()方法返回的确实是一个String[]数组,而不是简单的Object对象,如果需要将array1当成String[]数组使用,则必须使用强制类型转换,奇怪的是,Array的newInstance()方法的方法签名是:
public static Object newInstance(Class<?> componentType, int... dimensions)
,在这个方法签名中使用了Class<?>泛型,但并没有真正利用这个泛型,如果将该方法改成public static <T> T[] newInstance(Class<T> componentType, int length)
这样就可以在调用该方法后无需进行强制类型转换了,但它只支持一维数组的创建
import java.lang.reflect.*;
import java.lang.annotation.*;
public class CrazyitArray
{
// 对Array的newInstance方法进行包装
@SuppressWarnings("unchecked")
public static <T> T[] newInstance(Class<T> componentType, int length)
{
return (T[]) Array.newInstance(componentType, length); // ①
}
public static void main(String[] args)
{
// 使用CrazyitArray的newInstance()创建一维数组
String[] arr = CrazyitArray.newInstance(String.class, 10);
// 使用CrazyitArray的newInstance()创建二维数组
// 在这种情况下,只要设置数组元素的类型是int[]即可。
int[][] intArr = CrazyitArray.newInstance(int[].class, 5);
arr[5] = "我自横刀向天笑";
// intArr是二维数组,初始化该数组的第二个数组元素
// 二维数组的元素必须是一维数组
intArr[1] = new int[] {23, 12};
System.out.println(arr[5]);
System.out.println(intArr[1][1]);
}
}
使用反射获取泛型信息
通过指定类对应的Class对象,可以获得该类里包含的所有成员变量,不管该成员变量是使用private修饰,还是使用public修饰,获得了成员变量对应的Field对象后,就可以获得该成员变量的数据类型
// 获得成员变量f的类型
Class<?> a = f.getType()
但这种方式只对普通类型的成员变量有效,如果该成员变量的类型是有泛型类型的类型,例如Map<String, Integer>类型,应使用如下方法来获得该成员变量的泛型类型
// 获得成员变量的f的泛型类型
Type gType = f.getGenericType();
然后将Type对象强制类型转换为ParameterizedType对象,ParameterizedType代表被参数化的类型,也就是增强了泛型限制的类型,ParameterizedType提供了两个方法:
- getRawType():返回没有泛型信息的原始类型
- getActualTypeArguments():返回泛型参数的类型
import java.util.*;
import java.lang.reflect.*;
public class GenericTest
{
private Map<String, Integer> score;
public static void main(String[] args)
throws Exception
{
Class<GenericTest> clazz = GenericTest.class;
Field f = clazz.getDeclaredField("score");
// 直接使用getType()取出的类型只对普通类型的成员变量有效
Class<?> a = f.getType();
// 下面将看到仅输出java.util.Map
System.out.println("score的类型是:" + a);
// 获得成员变量f的泛型类型
Type gType = f.getGenericType();
// 如果gType类型是ParameterizedType对象
if (gType instanceof ParameterizedType)
{
// 强制类型转换
var pType = (ParameterizedType) gType;
// 获取原始类型
Type rType = pType.getRawType();
System.out.println("原始类型是:" + rType);
// 取得泛型类型的泛型参数
Type[] tArgs = pType.getActualTypeArguments();
System.out.println("泛型信息是:");
for (var i = 0; i < tArgs.length; i++)
{
System.out.println("第" + i + "个泛型类型是:" + tArgs[i]);
}
}
else
{
System.out.println("获取泛型类型出错!");
}
}
}
执行结果为:
D:\CrazyJava\CrazyJava\codes\18\18.6>java GenericTest
score的类型是:interface java.util.Map
原始类型是:interface java.util.Map
泛型信息是:
第0个泛型类型是:class java.lang.String
第1个泛型类型是:class java.lang.Integer
Type也是java.lang.reflect包下的一个接口,该接口代表所有类型的公共高级接口,Class是Type接口的实现类,Type包括原始类型、参数化类型、数组类型、类型变量和基本类型等