百科问答小站 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#工作人员学习和使用JAVA? 
  低代码开发以后有前景么?会不会最后一地鸡毛? 
  你最喜欢的中国编程网站是哪些? 
  编程语言用let等关键字声明变量有什么好处? 
  C「带坏了」多少程序语言的设计? 
  前端开发的难点到底在什么地方? 
  既然每个程序占用的内存都是操作系统管理的,为什么内存泄漏还是个问题? 

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





© 2024-06-26 - tinynew.org. All Rights Reserved.
© 2024-06-26 - tinynew.org. 保留所有权利