• Java不支持创建范型数组分析


    我想这个问题的答案是:因为这样做会破坏类型安全。核心的问题在于Java范型和C#范型存在根本区别:Java的范型停留在编译这一层,到了运行时,这些范型的信息其实是被抹掉的;而C#的范型做到了MSIL这一层。Java的做法不必修改JVM,减少了潜在的大幅改动和随之而来的风险,也许同时也反映出Java Bytecode规范在设计之初的先天不足;C#则大刀阔斧,连CLR一起改以支持更彻底的范型,换句话说,在范型这一点上,感觉C#更C++一点。

    在Java中,Object[]数组可以是任何数组的父类,或者说,任何一个数组都可以向上转型成它在定义时指定元素类型的父类的数组,这个时候如果我们往里面放不同于原始数据类型 但是满足后来使用的父类类型的话,编译不会有问题,但是在运行时会检查加入数组的对象的类型,于是会抛ArrayStoreException:

    1 String[] strArray = new String[20];
    2 Object[] objArray = strArray;
    3 objArray[0] = new Integer(1); // throws ArrayStoreException at runtime

    因为Java的范型会在编译后将类型信息抹掉,这样如果Java允许我们使用类似

     Map<Integer, String>[] mapArray = new Map<Integer, String>[20];

    这样的语句的话,我们在随后的代码中可以把它转型为Object[]然后往里面放Map<Double, String>实例。这样做不但编译器不能发现类型错误,就连运行时的数组存储检查对它也无能为力,它能看到的是我们往里面放Map的对象,我们定义的<Integer, String>在这个时候已经被抹掉了,于是而对它而言,只要是Map,都是合法的。想想看,我们本来定义的是装Map<Integer, String>的数组,结果我们却可以往里面放任何Map,接下来如果有代码试图按原有的定义去取值,后果是什么不言自明。

    所以,Java编译器不允许我们new范型数组。

     

     

    大家都知道Java是不能够声明泛型数组的,诸如以下的语法是不支持的:

     
    List<String>[] ls = new ArrayList<String>[10];

    而这样声明却是可以的:

     
     List<String>[] ls = new ArrayList[10] 

     但是我一直不清楚为什么不能够声明泛型的数组,指定类型可以让编译的时候不会出现类型安全的提示。

    直到今天我看到Sun的一篇文档才清楚,里面提到了一种情况:

    List<String>[] lsa = new List<String>[10]; // Not really allowed.  
    Object[] oa = (Object[]) lsa;  
    
    List<Integer> li = new ArrayList<Integer>();  
    
    li.add(new Integer(3));  
    
    oa[1] = li; // Unsound, but passes run time store check   
    
    String s = lsa[1].get(0); // Run-time error: ClassCastException. 
     这种情况下,由于JVM泛型的擦除机制,在运行时JVM是不知道泛型信息的,所以可以给oa[1]赋上一个ArrayList<Integer>而不会出现ArrayStoreException,但是在取出数据的时候却要做一次类型转换,所以就会出现ClassCastException,如果可以进行泛型数组的声明,上面说的这种情况在编译期将不会出现任何的警告和错误,只有在运行时才会出错。而对泛型数组的声明进行限制,对于这样的情况,可以在编译期提示代码有类型安全问题,比没有任何提示要强很多。

    基于以上的原因,Java不支持声明泛型数组,更确切地表达是:数组的类型不可以是类型变量,除非是采用通配符的方式,看下面这个例子: 

    List<?>[] lsa = new List<?>[10]; // OK, array of unbounded wildcard type.  
    
    Object[] oa = (Object[]) lsa;  
    
    List<Integer> li = new ArrayList<Integer>();  
    
    li.add(new Integer(3));  
    
    oa[1] = li; // Correct.  
    
    String s = (String) lsa[1].get(0); // Run time error, but cast is explicit. 

    因为对于通配符的方式,最后取出数据是要做显式的类型转换的,所以并不会存在上一个例子的问题。

    努力奋斗的小墨鱼 ---- http://www.cnblogs.com/WayneZeng/
  • 相关阅读:
    2018.5.27 OraclePLSQL编程 if-else练习和循环结构练习
    桶排序
    从函数中返回引用
    函数返回值
    参数传递模式
    计数排序(不基于比较的排序算法)
    快排序
    堆用作优先队列
    堆排序
    合并算法
  • 原文地址:https://www.cnblogs.com/WayneZeng/p/2697296.html
Copyright © 2020-2023  润新知