百科问答小站 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语言后应该学什么? 
  如何有效阅读Github上开源项目代码? 
  如何看待阿里巴巴Java开发程序员孤尽觉得Java是世界上最好的语言? 
  想知道那些编程大佬是不是代码都能记住,然后不用查百度就能啪啪啪打出来呢? 
  如何评价11岁编程「天才少女」万海妍? 
  如何通俗地理解「分布式系统」,它解决了哪些问题,有什么优缺点? 
  编程的世界是什么样的? 
  为什么我用c++写的游戏那么简陋? 
  世界顶级开源软件工程师,他们如何谋利? 
  靠写代码能买得起一辆奥迪A8吗(中国)? 

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





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