J2SE 1.5提供了“Autoboxing”和“Auto-Unboxing”的機制,可以讓編譯器來自動完成在基本類型和它們的包裹對象之間的轉化工作,從而能夠用一種更簡單的方式,來避免同時存在兩套類型系統所帶來的一些麻煩。本文介紹Autoboxing/Auto-Unboxing機制的使用方法、實質、發生時機、局限、對重載機制的影響以及對性能的妨礙等問題。
傳統上,在Java程序中,可以往一個容器類(無論是Collection還是Map)裡直接放入一個對象;但是如果打算放入的是一個數字、字符或布爾值的話,就要先加入一個“生成包裹它們的對象”的步驟。
造成這種現象的原因是,在Java語言當中一直存在著兩套非常不同的類型系統:
一套是所謂的“引用類型”(Reference Types),包括所有的類和接口。這些類型的數據被看作對象,所以可以用一個Object型的變量來保存。
一套是所謂的“基本類型”(Primitive Types),包括:byte、short、int、long、float、double、char和boolean。這些類型的數據不是對象,因此也不能用Object型的變量來保存。
同時采用這樣兩套類型系統,可以得到一些性能方面的好處——因為基本類型的數據不是對象,所以創建得更快、占用的空間更少、收回它們占用的資源也更容易;但是,這樣的做法同時也會造成一些編碼方面的問題——例如,不能定義一個變量(或數組),讓它既能保存基本類型的數據,又能保存引用類型的數據(類似的,也不能定義一個同時能匹配這兩種類型的數據的形參,不過這個問題可以借助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”的概念之後,才得到了廣泛的接受。在那之前,它們似乎並沒有通用的、專門的名字。不過由於那時也很少提及這兩個概念,所以這個問題倒也沒有造成什麼嚴重的影響。