站内搜索: 请输入搜索关键词

当前页面: 开发资料首页Java 专题逐渐挖掘Autoboxing-Auto-Unboxing

逐渐挖掘Autoboxing-Auto-Unboxing

摘要: 逐渐挖掘Autoboxing-Auto-Unboxing

</td> </tr> <tr> <td height="35" valign="top" class="ArticleTeitle"> <table width="100%" border="0" cellspacing="0" cellpadding="0"> <tr> <td height="58" align="left" valign="top"> </td> </tr> </table>

逐渐挖掘Autoboxing/Auto-Unboxing
更简单的整合两套类型系统

J2SE 1.5提供了“Autoboxing”和“Auto-Unboxing”的机制,可以让编译器来自动完成在基本类型和它们的包裹对象之间的转化工作,从而能够用一种更简单的方式,来避免同时存在两套类型系统所带来的一些麻烦。本文介绍Autoboxing/Auto-Unboxing机制的使用方法、实质、发生时机、局限、对重载机制的影响以及对性能的妨碍等问题。

传统上,在Java程序中,可以往一个容器类(无论是Collection还是Map)里直接放入一个对象;但是如果打算放入的是一个数字、字符或布尔值的话,就要先加入一个“生成包裹它们的对象”的步骤。

造成这种现象的原因是,在Java语言当中一直存在着两套非常不同的类型系统:

同时采用这样两套类型系统,可以得到一些性能方面的好处——因为基本类型的数据不是对象,所以创建得更快、占用的空间更少、收回它们占用的资源也更容易;但是,这样的做法同时也会造成一些编码方面的问题——例如,不能定义一个变量(或数组),让它既能保存基本类型的数据,又能保存引用类型的数据(类似的,也不能定义一个同时能匹配这两种类型的数据的形参,不过这个问题可以借助Java里的重载机制来回避)。

实际上需要定义“不知道用来保存什么类型的数据”的变量(和形参)时,一般对这个问题采取回避的态度,将它们的类型定义成Object,然后借助可以称为“Boxing”和“Unboxing”的操作来解决Object不能涵盖基本类型的问题。

1. Boxing和Unboxing操作

所谓Boxing操作,是指通过生成一个能包裹基本类型数据的对象,来让基本类型的数据出现在只能接受引用类型的地方。

清单1:手工Boxing的典型情况

Collection integers = new ArrayList();
for(int i = 0; i < 10; i++) {
integers.add(new Integer(i));
}

用于生成这些的对象的类,被称作“包裹类”(Wrapper Classes)。Java中的包裹类有Byte 、Short、Integer、Long、Float、Double、Character和Boolean(都在java.lang包里定义)等八种,分别用于包裹byte、short、int、long、float、double、char和boolean类型的数据。

而所谓Unboxing操作,则是指调用包裹类对象的相应方法,得到它们所代表的“基本类型的数据”,以便进行进一步的处置。

清单2:手工Unboxing的典型情况

for(Iterator itr = integers.iterator(); itr.hasNext(); ) {
Integer i = (Integer) itr.next();
System.out.println(i.intValue() + 1);
}

而在Java语言的最新版本——J2SE 1.5中,提供了“Autoboxing”和“Auto-Unboxing”的机制,可以让编译器来自动完成这些琐碎的操作,从而用一种更简单的方式,来整合两套类型系统。

熟悉的陌生名词

尽管这一对操作的历史很悠久,但是把它们称作“Boxing”和“Unboxing”的做法,基本是在出现“Autoboxing”和“Auto-Unboxing”的概念之后,才得到了广泛的接受。在那之前,它们似乎并没有通用的、专门的名字。不过由于那时也很少提及这两个概念,所以这个问题倒也没有造成什么严重的影响。

2. 使用Autoboxing和Auto-Unboxing

使用Autoboxing和Auto-Unboxing,并不需要什么特别的步骤,一切都会在编译器的安排下自动发生。

现在可以这样来对待一个int型的数据:

清单3:自动完成的Boxing操作

Collection al = new ArrayList();
al.add(1);

因为编译器会悄悄的把这段代码转换成接近这个样子:

清单4:作了Autoboxing之后的等价形式

Collection al = new ArrayList();
al.add(Integer.valueOf(1));

这里所用的能接受int类型的值为参数,生成Integer实例的valueOf方法,是J2SE 1.5中新加入的内容。其它包裹类也都有可以接受对应的基本类型的值为参数,生成对应的包裹类实例的valueOf方法加入。

而这样对待一个Integer型的对象也是可以的:

清单5:自动完成的Unboxing操作

Integer one = new Integer(1);
int two = one + 1;

因为编译器会悄悄的把这段代码转换成类似这个形状:

清单6:作了Auto-Unboxing之后的等价形式

Integer one = new Integer(1);
int two = one.intValue() + 1;

大体上,只要把一个结果类型是基本类型的表达式,放到需要让它们的包裹类出现的位置上,就会诱发Autoboxing;类似的,只要把一个结果类型是包裹类的表达式,放到只允许相应的基本类型出现的位置上,就会诱发Auto-Unboxing。