JAVA泛型中的类型擦除及为什么不支持泛型数组

简介:

一,数组的协变性(covariant array type)及集合的非协变性

设有Circle类和Square类继承自Shape类。

关于数组的协变性,看代码:

复制代码
public static double totalArea(Shape[] arr){
        double total = 0;
        for (Shape shape : arr) {
            if(shape != null)
                total += shape.area();
        }
        return total;
    }
复制代码

如果给 totalArray(Shape[] arr) 传递一个Circle[] 类型的数组,这是可以的,编译通过,也能正常运行。也就是说:Circle[] IS-A Shape[]

 

关于集合的协变性,看代码:

复制代码
public static double totalArea(Collection<Shape> arr){
        double total = 0;
        for (Shape shape : arr) {
            if(shape != null)
                total += shape.area();
        }
        return total;
    }
复制代码

如果给totalArea(Collection<Shape> arr)传递一个 Collection<Circle>类型的集合,这是不可以的。编译器就会报如下的错误:

The method totalArea(Collection<Shape>) in the type Demo is not applicable for the arguments (Collection<Circle>)

也就是说:Collection<Circle> IS-NOT-A Collection<Shape>

 

二,如果解决集合的非协变性带来的不灵活?

出现了泛型!

复制代码
public static double totalArea(Collection<? extends Shape> arr){
        double total = 0;
        for (Shape shape : arr) {
            if(shape != null)
                total += shape.area();
        }
        return total;
    }
复制代码

这样,就可以给totalArea(Collection<? extends Shape> arr)

传递Collection<Circle>、Collection<Square>、Collection<Shape>类型的参数了。

 

三,泛型的类型擦除及类型擦除带来的ClassCastException异常

JAVA的泛型只存在于编译层,到了运行时,是看不到泛型的。

还是拿数组来做对比:

 

1 String[] str = new String[10];
2 Object[] obj = str;//向上转型
3 
4 //Exception in thread "main" java.lang.ArrayStoreException: java.lang.Integer
5 obj[0] = new Integer(2);

 

第5行代码在运行时会抛第4行中表示的异常。

 

再来看泛型:

复制代码
 1         ArrayList<Integer> intList = new ArrayList<Integer>();
 2         intList.add(2);
 3         Object obj = intList;
 4         
 5         //Type safety: Unchecked cast from Object to ArrayList<String>
 6         ArrayList<String> strList = (ArrayList<String>)obj;
 7         
 8         //Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
 9         String str = strList.get(0);
10         str.trim();//do something with str
复制代码

编译器会对第6行提示第5行所示的警告。程序运行到第9行时抛出ClassCastException异常。因为ArrayList存储的本质上是一个Integer。

现在分析下第6行代码:

obj是Object类型的引用,strList是一个ArrayList<String>类型的引用,因此,向下转型时编译器给出了警告,在运行时,由于类型擦除,相当于

ArrayList strList = (ArrayList)obj;

因此,代码运行到第6行也能通过。

对于第9行代码:

strList是一个ArrayList<String>类型的引用,当然可以调用 ArrayList的get方法。因此,编译时没问题。但在运行时,

由于,String str = strList.get(0);会编译成String str = (String)strList.get(0);

而strList.get(0)得到 的是一个Integer对象,然后把它赋值给 String str,由于Integer IS-NOT-A String。故抛出ClassCastException。

参考:

 

四,为什么不支持泛型数组

参考下面代码:

复制代码
1         //Cannot create a generic array of ArrayList<Integer>
2         ArrayList<Integer>[] intArr = new ArrayList<Integer>[10];
3         Object[] obj = intArr;
4         
5         ArrayList<String> listStr = new ArrayList<String>();
6         obj[0] = listStr;
7         
8         ArrayList<Integer> listInt = intArr[0];
9         Integer i = listInt.get(0);//想要Integer,但却是String
复制代码

假设允许泛型数组,那么第2行是正确的,那么将不会有第1行中所示的编译错误。

那么就可以将 intArr 转型成 Object[],然后向Object[]放 ArrayList<String>,而不是我们想要的ArrayList<Integer>

因此,在运行时,类型是擦除的,运行时系统无法对数组中存储的类型做检查。它看到仅是:向intArr数组里面放 ArrayList对象。

相当于:

//        ArrayList<String> listStr = new ArrayList<String>();
        ArrayList listStr = new ArrayList();//运行时看到的情况
//        ArrayList<Integer> listInt = intArr[0];
        ArrayList listInt = intArr[0];//运行时看到的情况

 

在上面第9行,如果改成:

Object o = listInt.get(0);

//do something with o

我们以为Object o 它实际引用 的是Integer类型的,但它底层却是String类型的,如果调用 hashCode(),我们以为它执行的是Integer的hashCode(),但它执行的是String的hashCode(),那意味着发现不了错误。。。。。。。

因此,JAVA不支持泛型数组。


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

相关文章
|
3月前
|
存储 缓存 Java
Java数组全解析:一维、多维与内存模型
本文深入解析Java数组的内存布局与操作技巧,涵盖一维及多维数组的声明、初始化、内存模型,以及数组常见陷阱和性能优化。通过图文结合的方式帮助开发者彻底理解数组本质,并提供Arrays工具类的实用方法与面试高频问题解析,助你掌握数组核心知识,避免常见错误。
|
3月前
|
安全 IDE Java
Java记录类型(Record):简化数据载体类
Java记录类型(Record):简化数据载体类
383 120
|
1月前
|
存储 算法 安全
Java集合框架:理解类型多样性与限制
总之,在 Java 题材中正确地应对多样化与约束条件要求开发人员深入理解面向对象原则、范式编程思想以及JVM工作机理等核心知识点。通过精心设计与周密规划能够有效地利用 Java 高级特征打造出既健壮又灵活易维护系统软件产品。
68 7
|
2月前
|
安全 Java
Java之泛型使用教程
Java之泛型使用教程
220 10
|
2月前
|
Java 开发者
Java 函数式编程全解析:静态方法引用、实例方法引用、特定类型方法引用与构造器引用实战教程
本文介绍Java 8函数式编程中的四种方法引用:静态、实例、特定类型及构造器引用,通过简洁示例演示其用法,帮助开发者提升代码可读性与简洁性。
|
2月前
|
Java
Java 数组学习笔记
本文整理Java数组常用操作:遍历、求和、查找、最值及二维数组行求和等典型练习,涵盖静态初始化、元素翻倍、去极值求平均等实例,帮助掌握数组基础与应用。
|
3月前
|
安全 算法 Java
Java泛型编程:类型安全与擦除机制
Java泛型详解:从基础语法到类型擦除机制,深入解析通配符与PECS原则,探讨运行时类型获取技巧及最佳实践,助你掌握泛型精髓,写出更安全、灵活的代码。
|
前端开发 Java
java前端:删除数组中指定元素的方法
java前端:删除数组中指定元素的方法
243 1
|
10月前
|
存储 Java 索引
Java快速入门之数组、方法
### Java快速入门之数组与方法简介 #### 一、数组 数组是一种容器,用于存储同种数据类型的多个值。定义数组时需指定数据类型,如`int[]`只能存储整数。数组的初始化分为静态和动态两种: - **静态初始化**:直接指定元素,系统自动计算长度,如`int[] arr = {1, 2, 3};` - **动态初始化**:手动指定长度,系统给定默认值,如`int[] arr = new int[3];` 数组访问通过索引完成,索引从0开始,最大索引为`数组.length - 1`。遍历数组常用`for`循环。常见操作包括求和、找最值、统计特定条件元素等。