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不支持泛型数组。

参考:

http://www.blogjava.net/deepnighttwo/articles/298426.html

http://www.blogjava.net/sean/archive/2005/08/09/9630.html

时间: 2024-10-25 22:20:57

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

泛型中的类型擦除

通过反射理解泛型的本质(类型擦除) Java中的泛型是通过类型擦除来实现的.所谓类型擦除,是指通过类型参数合并,将泛型类型实例关联到同一份字节码上.编译器只为泛型类型生成一份字节码,并将其实例关联到这份字节码上.类型擦除的关键在于从泛型类型中清除类型参数的相关信息,并且再必要的时候添加类型检查和类型转换的方法. 下面通过两个例子来证明在编译时确实发生了类型擦除. 例1分别创建实际类型为String和Integer的ArrayList对象,通过getClass()方法获取两个实例的类,最后判断这个

java为什么要用类型擦除实现泛型?

为什么需要泛型? 试想你需要一个简单的容器类,或者说句柄类,比如要存放一个苹果的篮子,那你可以这样简单的实现: class Fruit{} class Apple extends Fruit{} class Bucket{ private Apple apple; public void set(Apple apple){ this.apple = apple; } java学习群669823128 public Apple get(){ return this.apple; } } 这样一个简

泛型中的类型约束和类型推断

前一篇文章介绍了泛型的基本概念.在本文中,我们看一下泛型中两个很重要的特性:类型约束和类型推断. 类型约束 相信你还记得前面一篇文章中的泛型方法,在这个泛型方法中,我们就使用了类型约束. 类型约束(type constraint)进一步控制了可指定的类型实参,当我们创建自己的泛型类型或者泛型方法的时候,类型约束是很有用的. 回到前一篇例子中的泛型方法,这个泛型方法就要求可指定的类型实参必须实现了IComparable接口. 为什么会有这个约束呢?原因很简单,因为我们在泛型方法的实现中直接调用T类

详解Java 8中Stream类型的“懒”操作

在进入正题之前,我们需要先引入Java 8中Stream类型的两个很重要的操作: 中间和终结操作(Intermediate and Terminal Operation) Stream类型有两种类型的方法: 中间操作(Intermediate Operation) 终结操作(Terminal Operation) 官方文档给出的描述为[不想看字母的请直接跳过]: Stream operations are divided into intermediate and terminal operat

Java进阶(四)Java反射TypeToken解决泛型运行时类型擦除的问题解决

在开发时,遇到了下面这条语句,不懂,然习之. private List<MyZhuiHaoDetailModel> listLottery = new ArrayList<MyZhuiHaoDetailModel>(); Gson gson=new Gson(); JSONObject object=new JSONObject(callbackValue); listLottery =  gson.fromJson(object.getString("lists&quo

Java进阶 四 Java反射TypeToken解决泛型运行时类型擦除问题

在开发时,遇到了下面这条语句,不懂,然习之. private List<MyZhuiHaoDetailModel> listLottery = new ArrayList<MyZhuiHaoDetailModel>(); Gson gson=new Gson(); JSONObject object=new JSONObject(callbackValue); listLottery =  gson.fromJson(object.getString("lists&quo

Java中的类型擦除与桥方法

类型擦除 Java在语法中虽然存在泛型的概念,但是在虚拟机中却没有泛型的概念,虚拟机中所有的类型都是普通类.无论何时定义一个泛型类型,编译后类型会被都被自动转换成一个相应的原始类型. 比如这个类 public class Parent<T> { public void sayHello(T value) { System.out.println("This is Parent Class, value is " + value); } } 在编译后就变成了 public c

Java泛型 泛型类的类型擦除

任何一个泛型类型,都对应这个一个原始类型.原始类型的名字来源于带参数的泛型类型名去掉参数后的结果,并将类中用到类型变量的地方替换为类型变量的限定类型(如果没有限定类型就用Object).下面是一个来源于<Java核心技术 卷1>的例子: 类型擦除前: package generic; /** * @version 1.00 2004-05-10 * @author Cay Horstmann */ public class Pair<T> { private T first; pr

swift中的&quot;类型擦除&quot;

在 Swift 的世界中,如果我们将协议称之为国王,那么泛型则可以视作皇后,所谓一山不容二虎,当我们把这两者结合起来使用的时候,似乎会遇到极大的困难.那么是否有一种方法,能够将这两个概念结合在一起,以便让它们成为我们前进道路上的垫脚石,而不是碍手碍脚的呢?答案是有的,这里我们将会使用到类型擦除 (Type Erasure) 这个强大的特性. Protocol 'SpellDelegate' can only be used as a generic constraint because it h