Java泛型由于只存在于编译时期,实际运行的类型还是Object类型,泛型数组也如下所示:
例如,我们试图定义一个泛型数组工具类,它可以用于按照顺序存入一个值,和指定位置取一个值,这实际上简易版的ArrayList,主要代码如下所示:
ackage genericArray;
/**
* 类GenericArray.java的实现描述:
* @author sweet 2012-6-17 下午7:23:28
*/
public class GenericArray<T> {
private T[] array;
@SuppressWarnings("unchecked")
public GenericArray(int size){
this.array=(T[])new Object[size];
}
public void put(int index,T data){
array[index]=data;
}
public T get(int index){
return array[index];
}
/**
* 这个方法会诱发 {@link java.lang.ClassCastException}<br/>
* 表明无论如何数组的运行类型只能是Object[]类型
*
* @return T[]
*/
public T[] rep(){
return array;
}
public static void main(String[] args){
GenericArray<Integer> genericArray=new GenericArray<Integer>(10);
genericArray.put(0, 11);
genericArray.put(1, 13);
System.out.println(genericArray.get(0));
System.out.println(genericArray.get(2));
try{
Integer[] integerArray=genericArray.rep();
System.out.println(integerArray);
}catch(Exception e){
System.out.println(e);
}
}
}
这段代码有个问题,就是它在初始化泛型数组的时候,使用了强制转型,这会导致一个编译时期的警告,虽然我们可以使用@SuppressedWarnings("unchecked")来在编译期绕过检查,但不是一个好办法。
所以下面一段代码对它稍稍改善了下,直接定义Object[]数组,在插入值做向上转型(这个在运行期间可以自动完成),和在取出值时向下转型(这个需要手动强制转型,并且会有编译期警告,但是由于我们只保留了一个可以用于插入值的入口,所以这个问题看上去没那么严重)
/**
* 类GenericeArrayOptimization.java的实现描述:<br/>
* 这个类试图利用Object[]去修复{@link GenericArray#rep()}方法所造成的泛型转换异常
* @author sweet 2012-6-17 下午7:44:07
* @see genericArray.GenericArray
*/
public class GenericeArrayOptimization<T> {
private Object[] objectArray;
public GenericeArrayOptimization(int size){
objectArray=new Object[size];
}
/**
* 这里只所以可以强转,是因为{@link GenericeArrayOptimization#put(int, Object)}<br/>
* 方法放入的对象都是T类型
*
* @param index
* @return
*/
@SuppressWarnings("unchecked")
public T get(int index){
return (T)objectArray[index];
}
public void put(int index,T data){
objectArray[index]=data;
}
/**
* 试图返回泛型数组的泛型<br/>
* 调用该方法会抛出{@link java.lang.ClassCastException}
*
* @return
*/
@SuppressWarnings("unchecked")
public T[] rep(){
return (T[])objectArray;
}
public static void main(String[] args){
GenericeArrayOptimization<Integer> array=new GenericeArrayOptimization<Integer>(10);
array.put(0, 11);
array.put(1, 13);
System.out.println(array.get(1));
try{
//实践证明,这种转换方式还是会抛出类型转换错误
Integer[] t=array.rep();
}catch(Exception e){
System.out.println(e);
}
}
}
从以上代码可以看出,没有任何方式可以推翻底层时候的数组类型,它在运行期间只能是Object类型,那么实际上有更好的办法,这个时候java.lang.reflect.Array.newInstance(Class<T> componentType,int length)就起到了作用。
我们先简单的看下JDK对这个方法的说明:
public static Object newInstance(Class<?> componentType,
int... dimensions)
throws IllegalArgumentException,
NegativeArraySizeException
创建一个具有指定的组件类型和维度的新数组。如果 componentType
表示一个非数组类或接口,则新数组具有 dimensions.length
维度,并且将 componentType
作为其组件类型。如果 componentType
表示一个数组类,则新数组的维数等于 dimensions.length
和 componentType
的维数的总和。在这种情况下,新数组的组件类型为 componentType
的组件类型。
新数组的维数不能超过该实现所支持的数组维数(通常为 255)。改造后的代码如下所示,用这个方法就可以创建我们真正需要的实际类型的数组。很可惜,在JDK类库里面,到处充斥着第二种方法的影子,不停的向上转型和向下转型。
/**
* 类GenerayArrayWithTypeToken.java的实现描述:<br/>
* 由{@link java.lang.reflect.Array#newInstance(Class, int)}<br/>
* 来实现泛型数组的返回,可参考{@link GenericeArrayOptimization#rep()}
* @author sweet 2012-6-24 上午11:28:02
*/
public class GenerayArrayWithTypeToken<T> {
private T[] array;
@SuppressWarnings("unchecked")
public GenerayArrayWithTypeToken(Class<T> componentType,int length){
array=(T[])Array.newInstance(componentType, length);
}
public void put(int index,T data){
this.array[index]=data;
}
public T get(int index){
return this.array[index];
}
public T[] rep(){
return this.array;
}
public static void main(String[] args){
GenerayArrayWithTypeToken<Integer> genericArray=new GenerayArrayWithTypeToken<Integer>(Integer.class,10);
genericArray.put(0, 11);
genericArray.put(1, 31);
System.out.println(Arrays.toString(genericArray.rep()));
}
}