`
android_mylove
  • 浏览: 399470 次
社区版块
存档分类
最新评论

Java 数组转型和范型

 
阅读更多

今天写代码遇到一个奇怪的问题,代码结构如下:

这样写代码个人觉得应该没什么问题,编译也没有问题。可是具体运行的时候报异常,如下:

Exception in thread "main"java.lang.ClassCastException: [Ljava.lang.Object;


但是如果这样写就没有问题:



这个问题怎么解释呢?

Java中允许向上和向下转型,但是这个转型是否成功是根据Java虚拟机中这个对象的类型来实现的。Java虚拟机中保存了每个对象的类型,而数组也是一个对象。

数组的类型是[Ljava.lang.Object,把[Ljava.lang.Object转换成[Ljava.lang.String是显然不可能的事情,因为这是一个向下转型,而虚拟机只保存了这是一个Object的数组,不能保证数组中的元素是String的,所以这个转型不能成功。数组里面的元素只是元素的引用,不是存储的具体元素,所以数组中元素的类型还是保存在Java虚拟机中的。

根据上面的解释,我们可以把这个问题归纳到下面这个模型。

Object objs[]=new Object[10];

String strs[]=(String[])objs;

这样子和刚才上面编译错误是一样的,如果我们把修改一下这个代码,如下:

String strs[]=new String[10];

Object objs[]=strs;

这样子就可以编译通过了,所以这个问题我们可以归结为一个Java转型规则的一个问题。


Java数组对范型的支持问题:

JDK5中,已经有了对泛型的支持,这样可以保证在集合和Map中的数据类型的安全,可是List的toArray方法返回的竟然是Object []让我很迷惑。个人感觉应该可以根据范型,直接返回相应的T []。仔细看了一下JDK的源码发现List转化为array有两个方法:

public Object[] toArray();

这个方法把List中的全部元素返回一个相同大小的数组,数组中的所有元素都为Object类型。

public <T> T[] toArray(T[] a);

这个方法把List中的全部元素返回一个相同大小的数组,数组中的所有元素都为T类型。

List如此设计是因为Java编译器不允许我们new范型数组,也就是说你不能这么定义一个数组:

T arr=new T[size];

但是你却可以用T[]来表示数组,而且可以把数组强制转化为T[]的。比如List中的public <T> T[] toArray(T[] a)是这么实现的:

从上面代码中可以看到,你必须通过反射来创建这个数组,因为你不知道这个数组的类型。a.getClass().getComponentType()方法是取得一个数组元素的类型。


Java为什么不支持创建范型数组?

我想这个问题的答案是:这样做会破坏类型安全,其核心的问题在于Java范型和C#范型存在根本区别:

Java的范型停留在编译这一层,到了运行时,这些范型的信息其实是被抹掉的;而C#的范型做到了MSIL(Microsoft Intermediate Language,微软中间语言)这一层。

Java的做法不必修改JVM,减少了潜在的大幅改动和随之而来的风险,也许同时也反映出Java Bytecode规范在设计之初的先天不足;

C#则大刀阔斧,连CLR(Common Language Runtime,公共语言运行时)一起改以支持更彻底的范型,换句话说,在范型这一点上,感觉C#更像C++。

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

String[] strArray =newString[20];

Object[] objArray = strArray;

objArray[0] =newInteger(1); // throws ArrayStoreException at runtime


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

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

这样的语句的话,我们在随后的代码中可以把它转型为Object[],然后往里面放Map<Double, String>实例。

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

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




toArray()两种实现方式

toArray() 源码,请参见我在google code 上传的 sdk 源码:src-jdk1.7.0_02



分享到:
评论

相关推荐

    Java中数组协变和范型不变性踩坑记录

    在Java编程语言中,数组和泛型是两种重要的数据结构,它们在处理类型安全方面有着不同的规则。本文主要探讨的是Java中的数组协变性和泛型的不变性,并通过实例揭示了这两个特性可能导致的问题。 首先,我们需要理解...

    数组、集合对象和范型

    在C#编程中,数组、集合对象和泛型是核心概念,它们在处理数据和构建高效应用程序时扮演着重要角色。 首先,数组是最基础的数据结构,用于存储同一类型的多个元素。在C#中,数组是一种固定大小的内存块,可以一次性...

    Java 范型Java 范型.doc

    Java 范型Java 范型

    Java程序设计范型和枚举PPT教案学习.pptx

    Java程序设计范型和枚举是Java编程中的关键概念,它们极大地增强了代码的类型安全性和重用性。本文将深入探讨这两个主题。 首先,我们来看什么是范型(Generics)。范型是Java SE 5.0引入的一个特性,它允许在类、...

    Java 范型攻略篇

    然而,通过在编译时进行严格的类型检查,Java范型仍然能够有效地防止类型不匹配的问题,从而保证了代码的健壮性和安全性。 总之,Java范型的引入解决了早期集合框架中类型信息丢失的问题,通过参数化类型实现了更高...

    泛型自定义数组大小

    在Java编程中,"泛型自定义数组大小"是一个重要的概念,它涉及到数据结构和算法的基础,以及面向对象编程中的类型安全。泛型是Java 5引入的一个特性,旨在提高代码的类型安全性,减少类型转换的冗余,并提供编译时的...

    JAVA范型指南中文版

    Java 泛型是一种在编程中实现强类型检查和减少冗余类型转换的机制,它是在JDK 1.5版本中引入的。泛型的主要目标是提高代码的类型安全性、可读性和重用性,避免在运行时出现类型转换异常。 1. **泛型的基本概念** -...

    用Java Socket实现一个简单的基于P2P范型的即时聊天系统。

    在本文中,我们将深入探讨如何使用Java的Socket编程来实现一个简单的基于P2P(Peer-to-Peer)范型的即时聊天系统。P2P网络架构允许每个节点既是客户端也是服务器,这种模式使得数据传输更加分散,提高了系统的可扩展...

    范型程序设计与 STL.pdf

    《范型程序设计与 STL》是一本深入探讨C++编程中的关键概念和技术的书籍,主要聚焦于范型(Generic Programming)和标准模板库(Standard Template Library,简称STL)。范型编程是一种强大的软件开发方法,它允许...

    《分布式系统原理与范型》.pdf

    总体而言,《分布式系统原理与范型》不仅为信息技术学科和电气工程学科的学生和教师提供了一本权威的学习参考书,也为从事相关技术领域的工程师和技术人员提供了自学读物。本书的出版,对提升我国在分布式系统领域的...

    分布式系统原理和范型第二版

    分布式系统原理和范型是计算机科学中的重要领域,尤其对于中高级程序员来说,深入理解这一概念至关重要。在当今云计算和大数据的时代,分布式系统的应用已经无处不在,从搜索引擎到社交媒体,从在线购物平台到物联网...

    C++多范型设计

    C++的范型(Template)是其强大的特性之一,它允许开发者创建泛化的类和函数,这些实体能够在编译时根据不同的类型参数实例化。这使得程序员能够编写出不依赖具体数据类型的通用代码,提升了代码的灵活性和可维护性...

    Java 实现泛型List

    Java 实现泛型List的源码,基本实现了List接口的全部所有方法。欢迎大家发表自己的观点和建议。

    第9章 范型和容器类.ppt

    在Java编程语言中,范型(Generics)和容器类是重要的核心概念,它们极大地提高了代码的可读性、安全性和复用性。本章主要探讨了如何利用这些概念来处理动态数据集合。 首先,传统的数组在Java中存在一些限制,如数...

    生成静态页面及范型的代码例子

    在IT行业中,生成静态页面和使用范型是两种常见的编程技术,主要应用于Web开发和软件设计。静态页面是指不依赖服务器端动态处理的HTML文件,它们在用户请求时不需要额外的计算,直接由Web服务器发送给浏览器。而范型...

    论文研究-消息传递范型与C/S范型双范型的主数据管理机制 .pdf

    本文提出的基于消息传递范型和客户机/服务器(Client/Server,简称C/S)范型双范型的主数据管理机制,能够有效解决MDM面临的问题。 消息传递范型是计算机科学中的一个基本概念,用于描述进程间通信的方式。在这范型...

    p2p聊天工具

    2. **Java编程语言**:本项目采用Java作为开发语言,Java以其跨平台性、丰富的类库和强大的网络支持而被广泛用于开发桌面应用。对于初学者,Java的面向对象特性也使其易于理解和实现复杂的功能。 3. **GUI设计**:...

    分布式系统原理与范型

    《分布式系统原理与范型》是分布式系统中的经典教材,全书分为两部分:原理和范型。第一部分详细讨论了分布式系统的原理、概念和技术,其中包括通信、进程、命名、同步、一致性和复制、容错以及安全。第二部分给出了...

    范型参考 (1).java

    范型参考 (1).java

Global site tag (gtag.js) - Google Analytics