語法糖(Syntactic Sugar),也稱糖衣語法,是由英國計算機學家 Peter.J.Landin 發(fā)明的一個術(shù)語,指在計算機語言中添加的某種語法,這種語法對語言的功能并沒有影響,但是更方便程序員使用。Java 中最常用的語法糖主要有泛型、變長參數(shù)、條件編譯、自動拆裝箱、內(nèi)部類等。虛擬機并不支持這些語法,它們在編譯階段就被還原回了簡單的基礎(chǔ)語法結(jié)構(gòu),這個過程成為解語法糖。
泛型是 JDK1.5 之后引入的一項新特性,Java 語言在還沒有出現(xiàn)泛型時,只能通過 Object 是所有類型的父類和類型強制轉(zhuǎn)換這兩個特點的配合來實現(xiàn)泛型的功能,這樣實現(xiàn)的泛型功能要在程序運行期才能知道 Object 真正的對象類型,在 javac 編譯期,編譯器無法檢查這個 Object 的強制轉(zhuǎn)型是否成功,這便將一些風險轉(zhuǎn)接到了程序運行期中。
Java 語言在 JDK1.5 之后引入的泛型實際上只在程序源碼中存在,在編譯后的字節(jié)碼文件中,就已經(jīng)被替換為了原來的原生類型,并且在相應(yīng)的地方插入了強制轉(zhuǎn)型代碼,因此對于運行期的Java語言來說,ArrayList和 ArrayList就是同一個類。所以泛型技術(shù)實際上是 Java 語言的一顆語法糖,Java 語言中的泛型實現(xiàn)方法稱為類型擦除,基于這種方法實現(xiàn)的泛型被稱為偽泛型。
下面是一段簡單的 Java 泛型代碼:
Map<Integer,String> map = new HashMap<Integer,String>();
map.put(1,"No.1");
map.put(2,"No.2");
System.out.println(map.get(1));
System.out.println(map.get(2));
將這段 Java 代碼編譯成 Class 文件,然后再用字節(jié)碼反編譯工具進行反編譯后,將會發(fā)現(xiàn)泛型都變回了原生類型,如下面的代碼所示:
Map map = new HashMap();
map.put(1,"No.1");
map.put(2,"No.2");
System.out.println((String)map.get(1));
System.out.println((String)map.get(2));
為了更詳細地說明類型擦除,再看如下代碼:
import java.util.List;
public class FanxingTest{
public void method(List<String> list){
System.out.println("List String");
}
public void method(List<Integer> list){
System.out.println("List Int");
}
}
當用 javac 編譯器編譯這段代碼時,報出了如下錯誤:
FanxingTest.java:3: 名稱沖突:method(java.util.List<java.lang.String>) 和 method
(java.util.List<java.lang.Integer>) 具有相同疑符
public void method(List<String> list){
^
FanxingTest.java:6: 名稱沖突:method(java.util.List<java.lang.Integer>) 和 metho
d(java.util.List<java.lang.String>) 具有相同疑符
public void method(List<Integer> list){
^
2 錯誤
是因為泛型 List 和 List 編譯后都被擦除了,變成了一樣的原生類型 List,擦除動作導(dǎo)致這兩個方法的特征簽名變得一模一樣,在 Class 類文件結(jié)構(gòu)一文中講過,Class 文件中不能存在特征簽名相同的方法。
把以上代碼修改如下:
import java.util.List;
public class FanxingTest{
public int method(List<String> list){
System.out.println("List String");
return 1;
}
public boolean method(List<Integer> list){
System.out.println("List Int");
return true;
}
}
發(fā)現(xiàn)這時編譯可以通過了(注意:Java 語言中 true 和 1 沒有關(guān)聯(lián),二者屬于不同的類型,不能相互轉(zhuǎn)換,不存在 C 語言中整數(shù)值非零即真的情況)。兩個不同類型的返回值的加入,使得方法的重載成功了。這是為什么呢?
我們知道,Java 代碼中的方法特征簽名只包括了方法名稱、參數(shù)順序和參數(shù)類型,并不包括方法的返回值,因此方法的返回值并不參與重載方法的選擇,這樣看來為重載方法加入返回值貌似是多余的。對于重載方法的選擇來說,這確實是多余的,但我們現(xiàn)在要解決的問題是讓上述代碼能通過編譯,讓兩個重載方法能夠合理地共存于同一個 Class 文件之中,這就要看字節(jié)碼的方法特征簽名,它不僅包括了 Java 代碼中方法特征簽名中所包含的那些信息,還包括方法返回值及受查異常表。為兩個重載方法加入不同的返回值后,因為有了不同的字節(jié)碼特征簽名,它們便可以共存于一個 Class 文件之中。
自動拆裝箱、變長參數(shù)等語法糖也都是在編譯階段就把它們該語法糖結(jié)構(gòu)還原為了原生的語法結(jié)構(gòu),因此在 Class 文件中也只存在其對應(yīng)的原生類型,這里不再一一說明。
更多建議: