包裝類型是一個類,它提供了諸如構造方法、類型轉換、比較等非常實用的功能,而且在Java5之後又實現了與基本類型之間的自動轉換,這使包裝類型如虎添翼,更是應用廣泛了,在開發中包裝類型已經隨處可見,但無論是從安全性、性能方面來說,還是從穩定性方面來說,基本類型都是首選方案。我們來看一段代碼:
package deep;
public class Client {
public static void main(String[] args) {
Client client = new Client();
int i = 140;
// 分別傳遞int類型和Integer類型
client.f(i);
client.f(Integer.valueOf(i));
}
private void f(long i) {
System.out.println("基本類型的方法被調用");
}
private void f(Long i) {
System.out.println("包裝類型的方法被調用");
}
}
運行結果:
基本類型的方法被調用
基本類型的方法被調用
client.f(i)的輸出是正常的,那第二個輸出就讓人困惑了,爲什麼會調用f(long a)方法呢?這是因爲自動裝箱有一個重要的原則:基本類型可以先加寬,再轉變成寬類型的包裝類型,但不能直接轉變成款類型的包裝類型。這句話比較拗口,簡單地說就是,int可以加寬轉變成long,然後再轉變成Long對象,但不能直接轉變成包裝類型,注意這裏指的都是自動轉換,你是通過構造函數生成。爲了解釋這個原則,我們再來看一個例子:
package deep;
public class Client {
public static void main(String[] args) {
int i = 140;
f(i);
}
private static void f(Long i) {
}
}
這段程序編譯時通不過的,因爲i是一個int類型,不能自動轉變成Long
型。但是修改成以下代碼就可以編譯通過了:
package deep;
public class Client {
public static void main(String[] args) {
int i = 140;
long l = (long) i;
f(l);
}
private static void f(Long i) {
}
}
這就是int先加寬轉變爲long型,然後自動轉換成Long型。規則說明白了,我們繼續來看f(Integer.valueOf(i))是如何調用的,Integer.valueOf(i)返回的是一個Integer對象,這沒錯,但是Integer和int是可以互相轉換的。沒有f(Integer i)方法?沒關係,編譯器會嘗試轉換成int類型的實參調用,OK,這次成功了,與f(i)相同了,於是乎被加寬轉變成long型——結果也很明顯了,整個f(Integer.valueOf(i))的執行過程是這樣的:
- i通過valueOf方法包裝成一個Integer對象。
- 由於沒有f(Integer i)方法,編譯器“聰明”地把Integer對象轉換成int
- int自動拓寬爲long,編譯結束
使用包裝類型確實有方便的地方,但是也會引起一些不必要的困惑,比如我們這個例子,如果f()的兩個重載方法使用的是基本類型,而且實參也是基本類型,就不會產生以上問題,而且程序的可讀性更強。自動裝箱(拆箱)雖然很方便,但引起的問題也非常嚴重——我們甚至都不知道執行的是哪個方法。