百科问答小站 logo
百科问答小站 font logo



java为什么不支持泛型数组? 第1页

  

user avatar   pang-pang-37-37 网友的相关建议: 
      

【感谢 @CNife 的指正,下面部分例子已经修正,欢迎大家继续给我挑错 :v】


首先Java号称自己是”类型安全“语言,不会意外怀孕。

       Girl g = new Boy(); //编译器爸爸不允许     


为了确保类型安全,Java数组就非常龟毛:必须明确知道内部元素的类型,而且会”记住“这个类型,每次往数组里插入新元素都会进行类型检查,不匹配会抛出java.lang.ArrayStoreException。

你可以把数组想像成小鸭子,把它第一眼见到的东西认作妈妈。比如下面这段代码,虽然声明的是Object[],但首次实例化成Integer[],这个数组小鸭子就记住了Integer是它妈妈,以后再往里填充String对象,它就不认了。

       Object[] ducks = new Integer[10]; // 小鸭子认定Integer是它妈妈 ducks[0] = "Hello"; // ERROR: ArrayStoreException     


但是!泛型有个什么问题呢?就是泛型是用擦除(Erasure)实现的,运行时类型参数会被擦掉。比如下面的例子,无论你声明的的是List<String>,还是List<Integer>或者原生类List,容器实际类型都是List<Object>所以泛型实际上都是狼外婆,它看上去像外婆,但实际上是大灰狼。

       // 以下三个容器实际类型都是List<Object> List<String> strList = new ArrayList<String>();  List<Integer> intList = new ArrayList<Integer>(); List rawList = new ArrayList();     


所以数组小鸭子遇到泛型狼外婆就要吃苦头了。对数组小鸭子Object[] 来说,GrandMother<RealGrandMother>GrandMother<Wolf>运行时看起来都是GrandMother。 那小鸭子岂不是要被吃掉? 所以有正义感的程序员哥哥就禁止掉了这件事。

           public static void main(String[] args) {         GrandMother<RealGrandMother>[] gm = new GrandMother<RealGrandMother>[3]; // 真外婆         Object[] ducks = gm; // 我们告诉数组小鸭子,只有见到外婆才能开门         ducks[0] = new GrandMother<Wolf>(); // 运行时狼外婆看起来和真外婆一模一样         RealGrandMother rgm = ducks[0].get(); // BOOM! 跳出一只狼外婆,小鸭子懵圈了     }      


唯一绕过限制,创建泛型数组的方式,是先创建一个原生类型数组,然后再强制转型。

       List[] ga = (List<Integer>[])new ArrayList[10];     


------------------------以下为原答案,对数组类型的探讨------------------------

Java Language Specification明确规定:数组内的元素必须是“物化”的。

It is a compile-time error if the component type of the array being initialized is not reifiable.


对“物化”的第一条定义就是不能是泛型:

A type is reifiable if and only if one of the following holds:
  • It refers to a non-generic class or interface type declaration.
  • ... ...



因为Array的具体实现是在虚拟机层面,嵌地非常深,也查不到源码。只好用javap反编译看看具体初始化数组的字节码。我们反编译下面一段代码:初始化一个String数组和一个int数组。

       String[] s=new String[]{"hello"}; int[] i=new int[]{1,2,3};      


反编译的片段如下:

          Code:        0: iconst_1        1: anewarray     #2                  // class java/lang/String        4: dup        5: iconst_0        6: ldc           #3                  // String hello        8: aastore        9: astore_1       10: iconst_3       11: newarray       int       13: dup       14: iconst_0       15: iconst_1       ... ...     

其中:

  • "1: anewarray #2":创建String数组
  • "11: newarray int":创建int数组

anewarray和newarray都是虚拟机内部用来创建数组的命令。最多只能有2的8次方256个操作码,光创建数组就占了不止一个,可见数组的地位有多特殊。


其中newarray用atype来标记数组类型。anewarray用index来标记。从描述里可以看到,数组除了元素类型,还有一个必须确定的是长度,因为数组是一段连续内存。


查一下 Java Virtual Machine 对anewarray命令的描述,

anewarray <type>
<type> indicates what types of object references are to be stored in the array. It is either the name of a class or interface, e.g. java/lang/String, or, to create the first dimension of a multidimensional array, <type> can be an array type descriptor, e.g.[Ljava/lang/String;

比如anewarray字节码命令的格式就是anewarray后面跟一个具体的元素类型。所以不能确定<type>的确切类型,就无法创建数组。




  

相关话题

  用 C++ 实现大整数的加减,思路是什么? 
  如何评价11岁编程「天才少女」万海妍? 
  java switch 不加 break 继续执行 下一个case(不用匹配条件) 这个设计是因为什么? 
  有什么像a=a+b;b=a-b;a=a-b;这样的算法或者知识? 
  C#的dynamic使用中有什么需要注意的地方,以免滥用? 
  怎么理解元编程? 
  计算机硕士不会编程怎么找工作? 
  如何看待「大部分程序员只会写三年代码」的说法? 
  忽然发现自己敲键盘的指法很不标准,我觉得已经对 coding 效率产生影响,怎么办? 
  大家做课题做项目进行科学研究的过程中编程产生的结果(数字,图像,表格等)都是暂时保存在哪里呢? 

前一个讨论
JAVA 学到什么水平就可以转战 Android 了?
下一个讨论
企业里的老员工对待刚入职的新员工的态度都是这么不友好的么?





© 2024-11-24 - tinynew.org. All Rights Reserved.
© 2024-11-24 - tinynew.org. 保留所有权利