zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    Flask莫名其妙特别慢
    MySQL老是提示视图没有主键
    Mysql写入中文出错
    Sqlite向MySql导入数据
    大智慧专业财务PFFIN(N,M)函数N的取值一览表
    js的技巧
    拍拍贷年化收益率的推算
    Sqlite的多表连接更新
    Kali Linux 64位架构安装Veil-Evasion
    修改Kali Linux 2020.1主题颜色
  • 原文地址:https://www.cnblogs.com/hapjin/p/5371950.html
Copyright © 2011-2022 走看看