Java 中的文法糖 (Syntactic Sugar)

來源:互聯網
上載者:User

        文法糖(Syntactic Sugar),也叫糖衣文法,是英國電腦科學家彼得·約翰·蘭達(Peter J. Landin)發明的一個術語。指的是,在電腦語言中添加某種文法,這種文法能使程式員更方便的使用語言開發程式,同時增強程式碼的可讀性,避免出錯的機會;但是這種文法對語言的功能並沒有影響。
Java中的泛型,變長參數,自動拆箱/裝箱,條件編譯等都是,下面做簡單的介紹和分析。

泛型
        與C#中的泛型相比,Java的泛型可以算是“偽泛型”了。在C#中,不論是在程式源碼中、在編譯後的中繼語言,還是在運行期泛型都是真實存在的。Java則不同,Java的泛型只在原始碼存在,只供編輯器檢查使用,編譯後的位元組碼檔案已擦除了泛型型別,同時在必要的地方插入了強制轉型的代碼。
泛型代碼:

public static void main(String[] args) {    List<String> stringList = new ArrayList<String>();    stringList.add("oliver");    System.out.println(stringList.get(0));}

        將上面的代碼的位元組碼反編譯後:

public static void main(String args[]){    List stringList = new ArrayList();    stringList.add("oliver");    System.out.println((String)stringList.get(0));}

自動拆箱/裝箱
        自動拆箱/裝箱是在編譯期,依據代碼的文法,決定是否進行拆箱和裝箱動作。
        裝箱過程:把基本類型用它們對應的封裝類型進行封裝,使基本類型具有對象特徵。
        拆箱過程:與裝箱過程相反,把封裝類型轉換成基本類型。
        需要注意的是:封裝類型的“==”運算在沒有遇到算數運算子的情況下不會自動拆箱,而其封裝類型的equals()方法不會處理資料類型轉換,所以:

Integer a = 1;Integer b = 1;Long c = 1L;System.out.println(a == b);System.out.println(c.equals(a));

    這樣的代碼應該盡量避免自動拆箱與裝箱。

迴圈曆遍(foreach)
       文法:
List<Integer> list = new ArrayList<Integer>();for(Integer num : list){    System.out.println(num);}

       Foreach要求被曆遍的對象要實現Iterable介面,由此可想而知,foreach迭代也是調用底層的迭代器實現的。反編譯上面源碼的位元組碼:

List list = new ArrayList();Integer num;Integer num;for (Iterator iterator = list.iterator(); iterator.hasNext(); System.out.println(num)){    num = (Integer) iterator.next();}


條件編輯
       很多程式設計語言都提供了條件編譯的途徑,C,C++中使用#ifdef。Java語言並沒有提供這種先行編譯功能,但是Java也能實現先行編譯。

if(true){    System.out.println("oliver");}else{    System.out.println("lee");}

       這段代碼的位元組碼反編譯後只有一條語句:

System.out.println("oliver");

       在編譯器中,將會把分支不成立的代碼消除,這一動作發生在編譯器解除文法糖階段。
       所以說,可以利用條件陳述式來實現先行編譯。枚舉
       枚舉類型其實並不複雜,在JVM位元組碼檔案結構中,並沒有“枚舉”這個類型。
       其實來源程式的枚舉類型,會在編譯期被編譯成一個普通了類。利用繼承和反射,這是完全可以做到的。
       看下面一個枚舉類:

public enum EnumTest {    OLIVER,LEE;}

       反編譯位元組碼後:

public final class EnumTest extends Enum {private EnumTest(String s, int i) {super(s, i);}public static EnumTest[] values() {EnumTest aenumtest[];int i;EnumTest aenumtest1[];System.arraycopy(aenumtest = ENUM$VALUES, 0,aenumtest1 = new EnumTest[i = aenumtest.length], 0, i);return aenumtest1;}public static EnumTest valueOf(String s) {return (EnumTest) Enum.valueOf(EnumTest, s);}public static final EnumTest OLIVER;public static final EnumTest LEE;private static final EnumTest ENUM$VALUES[];static {OLIVER = new EnumTest("OLIVER", 0);LEE = new EnumTest("LEE", 1);ENUM$VALUES = (new EnumTest[] { OLIVER, LEE });}}

       至於更多細節,可以參考父類Enum。

變長參數
       變長參數允許我們傳入到方法的參數是不固定個數。
       對於這個方法:

public void foo(String str,Object...args){}

       我們可以這樣調用:

foo("oliver");foo("oliver",new Object());foo("oliver",new Integer(1),"sss");foo("oliver",new ArrayList(),new Object(),true,1);

       參數args可以是任意多個。
       其實,在編譯階段,args是會被編譯成Object [] args。

public transient void foo(String s, Object aobj[]){}

       這樣,變長參數就可以實現了。
       但是要注意的是,變長參數必須是方法參數的最後一項。

       除了上面介紹的文法糖,還有內部類,斷言以及JDK7的switch支援字串,自動關閉資源(在try中定義和關閉)等。
感興趣的同學可以反編譯位元組碼瞭解它們的本質。
       無疑,文法糖方便了程式員的開發,提高了開發效率,提升了文法的嚴謹也減少了編碼出錯誤的幾率。我們不僅僅在平時的編碼中依賴文法糖,更要看清文法糖背後程式碼的真實結構,這樣才能更好的利用它們。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.