数组的真实类型必须是泛型类型的子类型

简介:

List接口的toArray方法可以把一个结合转化为数组,但是使用不方便,toArray()方法返回的是一个Object数组,所以需要自行转变.

toArray(T[] a)虽然返回的是T类型的数组,但是还是需要传入一个T类型的数组,这也挺麻烦的.我们期望输入的是一个泛型化的list,这样就能转化为泛型数组了.

看代码:

复制代码
 1 import java.util.Arrays;
 2 import java.util.List;
 3 
 4 public class Client<T> {
 5     public static <T> T[] toArray(List<T> list){
 6         T[] t = (T[])new Object[list.size()];
 7         for(int i=0,n=list.size();i<n;i++){
 8             t[i] = list.get(i);
 9         }
10         return t;
11     }
12     
13     public static void main(String[] args) {
14         List<String> list = Arrays.asList("A","B");
15         for(String str:toArray(list)){//这一句报错
16             System.out.println(str);
17         }
18     }
19 }
复制代码

编译没有任何问题,运行后出现如下异常;

Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
    at cn.summerchill.test.Client.main(Client.java:17)

类型转换异常,也就是说,不能把一个Object数组转换为String数组,这段异常包含了两个问题:

1.为什么Object数组不能向下转型为String数组?

数组是一个容器,只有确保容器内的所有元素类型与期望的类型有父子关系时才能转换,Object数组只能保证数组内的元素是Object类型.却不能确保它们都是String的父类型或子类,所以转换失败.

2.为什么是main方法抛出异常,而不是toArray方法?

其实,是在toArray方法中进行的类型向下转换,而不是main方法中,那为什么异常会在main方法中抛出,应该在toArrya方法的   T[] t = (T[])new Object[list.size()] 这段才对啊....

那是因为泛型是类型擦除的,toArray方法经过编译后与如下代码相同:

复制代码
 1 import java.util.Arrays;
 2 import java.util.List;
 3 
 4 public class Client<T> {
 5     public static Object[] toArray(List list){
 6         //此处的强制类型没必要存在,只是为了保持与源代码对比
 7         Object[] t = (Object[])new Object[list.size()];
 8         for(int i=0,n=list.size();i<n;i++){
 9             t[i] = list.get(i);
10         }
11         return t;
12     }
13     
14     public static void main(String[] args) {
15         List<String> list = Arrays.asList("A","B");
16         for(String str:(String[])toArray(list)){
17             System.out.println(str);
18         }
19     }
20 }
复制代码

阅读完此段代码就很清楚了,toArray方法返回后会进行一次类型转换,Object数组转换成了String数组,于是就报了ClassCastException异常了.

Object数组不能转换成String数组,T类型又无法在运行期获得,那该如何解决这个问题呢?

其实要想把一个Object数组转换成为String数组,只要Object数组的实际类型也是String就可以了.

复制代码
 1 public class Client<T> {
 2     public static void main(String[] args) {
 3         //objArray的实际类型和表面类型都是String数组
 4         Object[] objArray = {"A","B"};
 5         //抛出ClassCastException
 6         String[] strArray = (String[])objArray;        
 7     
 8         String[] ss = {"A","B"};
 9         //objs的真实类型是String数组,显示类型为Object数组
10         Object[] objs = ss;
11         //顺利转换为String数组
12         String[] strs = (String[])objs;        
13     }
14 }
复制代码

知道了上面,把泛型数组声明为泛型类的子类型

复制代码
 1 import java.lang.reflect.Array;
 2 import java.util.Arrays;
 3 import java.util.List;
 4 
 5 public class Client<T> {
 6 
 7     public static <T> T[] toArray(List<T> list, Class<T> tClass) {
 8         //声明并初始化一个T类型的数组
 9         T[] t = (T[]) Array.newInstance(tClass, list.size());        
10         for(int i=0,n=list.size();i<n;i++){
11             t[i] = list.get(i);
12         }
13         return t;
14     }
15     public static void main(String[] args) {
16         List<String> list = Arrays.asList("A", "B");
17         for (String str : toArray(list,String.class)) {
18             System.out.println(str);
19         }
20     }
21 }
复制代码

通过反射类Array声明了一个T类型的数组,由于我们无法在运行期获得泛型类型的参数,因此就需要调用者主动传入T参数类型.此时,客户端再调用就不会出现任何异常了.

在这里我们看到,一个泛型类(特别是泛型集合)转变为泛型数组时,泛型数组的真实类型不能是泛型类型的父类型(比如顶层类Object),只能是泛型类型的子类型(当然包含自身类型),否则就会出现类型转换异常.

 


本文转自SummerChill博客园博客,原文链接:http://www.cnblogs.com/DreamDrive/p/5626076.html,如需转载请自行联系原作者

相关文章
|
1月前
|
数据可视化 Java
让星星月亮告诉你,通过反射创建类的实例对象,并通过Unsafe theUnsafe来修改实例对象的私有的String类型的成员属性的值
本文介绍了如何使用 Unsafe 类通过反射机制修改对象的私有属性值。主要包括: 1. 获取 Unsafe 的 theUnsafe 属性:通过反射获取 Unsafe类的私有静态属性theUnsafe,并放开其访问权限,以便后续操作 2. 利用反射创建 User 类的实例对象:通过反射创建User类的实例对象,并定义预期值 3. 利用反射获取实例对象的name属性并修改:通过反射获取 User类实例对象的私有属性name,使用 Unsafe`的compareAndSwapObject方法直接在内存地址上修改属性值 核心代码展示了详细的步骤和逻辑,确保了对私有属性的修改不受 JVM 访问权限的限制
52 4
|
6月前
|
JavaScript 前端开发 编译器
TypeScript 中的基础类型:原始类型、对象类型、数组类型、元组类型、枚举类型和联合类型
TypeScript 中的基础类型:原始类型、对象类型、数组类型、元组类型、枚举类型和联合类型
74 1
引用传递示例,以及String类型如何传递的原理
大家都知道值传递和引用传递,下面我来通过以一个问题和示例来解决引用类型传递的原理         1)先创建了引用类型Person里面有String name 和int age          2)new 一个对象,把这个对象的值赋给a,再把a赋给Person类型的b
|
存储 Java
基本类型、包装类型、引用类型、String等作为实参传递后值会不会改变?
基本类型、包装类型、String类型作为参数传递之后,在方法里面修改他们的值,原值不会改变!引用类型不一定,要看是怎么修改它的。
114 0
|
Java 编译器
重载的方法能否根据返回类型进行区分?
重载的方法不能根据返回类型进行区分。方法的重载是基于方法名称和参数列表来进行区分的,与返回类型无关。这是因为在Java中,编译器在确定要调用哪个重载方法时,仅根据传递给方法的参数来进行决策。
366 0
1、原始类型与引用类型(区别)
1、原始类型与引用类型(区别)
142 0
|
JavaScript 前端开发
由一个问题引发关于对象和对象子类型的思考
由一个问题引发关于对象和对象子类型的思考
|
JavaScript 前端开发
证明函数是对象的特殊子类型和函数的对象属性
证明函数是对象的特殊子类型和函数的对象属性
抽象类的继承,接口的实现,接口类型数组的使用,根据instanceof判断(返回)是否该是哪一个类型,类型的强转.
总觉得之前第2处有点问题,果然. 还需要instanceof判定一下,然后还需要把数组Animal[]转为Pet的才有方法play()~~~!将编程看作是一门艺术,而不单单是个技术。 敲打的英文字符是我的黑白琴键, 思维图纸画出的是我编写的五线谱。
847 0
|
Java
思想:java中,父类的方法中传入的形参的数据类型是泛型,子类的方法的形参想只要一种确定的数据类型,子类该如何做呢?
解决问题的思想: /* * 思想: * java中,父类的方法传入的形参的数据类型是泛型,子类的方法的形参想只要一种确定的数据类型,子类该如何做呢? *   答:那么子类可以重新写一个方法,形参传入的是一种想要的数据类型,而子类方法里面调用的还是父类的方法。
1256 0