Java 中 Varargs 機制的理解
來源:原創(chuàng) 時間:2017-09-21 瀏覽:0 次
J2SE 1.5供給了“Varargs”機制。憑借這一機制,能夠界說能和多個實參相匹配的形參。然后,能夠用一種更簡略的辦法,來傳遞個數(shù)可變的實參。本文介紹這一機制的運用辦法,以及這一機制與數(shù)組、泛型、重載之間的相互作用時的若干問題。
到J2SE 1.4停止,一向無法在Java程序里界說實參個數(shù)可變的辦法——由于Java要求實參(Arguments)和形參(Parameters)的數(shù)量和類 型都有必要逐個匹配,而形參的數(shù)目是在界說辦法時就現(xiàn)已固定下來了。雖然能夠經(jīng)過重載機制,為同一個辦法供給帶有不同數(shù)量的形參的版別,可是這依然不能到達 讓實參數(shù)量恣意改變的意圖。
可是,有些辦法的語義要求它們有必要能承受個數(shù)可變的實參——例如聞名的main辦法,就需求能承受一切的命令行參數(shù)為實參,而命令行參數(shù)的數(shù)目,事前底子無法斷定下來。
關于這個問題,傳統(tǒng)上一般是選用“運用一個數(shù)組來包裹要傳遞的實參”的做法來敷衍。
用數(shù)組包裹實參
“用數(shù)組包裹實參”的做法能夠分紅三步:首要,為這個辦法界說一個數(shù)組型的參數(shù);然后在調(diào)用時,生成一個包含了一切要傳遞的實參的數(shù)組;最終,把這個數(shù)組作為一個實參傳遞曩昔。
這種做法能夠有用的到達“讓辦法能夠承受個數(shù)可變的參數(shù)”的意圖,僅僅調(diào)用時的辦法不行簡略。
J2SE 1.5中供給了Varargs機制,答應直接界說能和多個實參相匹配的形參。然后,能夠用一種更簡略的辦法,來傳遞個數(shù)可變的實參。
Varargs的意義
大體說來,“Varargs”是“variable number of arguments”的意思。有時分也被簡略的稱為“variable arguments”,不過由于這一種叫法沒有闡明是什么東西可變,所以意義略微有點含糊。
界說實參個數(shù)可變的辦法
只要在一個形參的“類型”與“參數(shù)名”之間加上三個接連的“.”(即“…”,英文里的句中省略號),就能夠讓它和不斷定個實參相匹配。而一個帶有這樣的形參的辦法,就是一個實參個數(shù)可變的辦法。
清單1:一個實參個數(shù)可變的辦法

留意,只要最終一個形參才干被界說成“能和不斷定個實參相匹配”的。因而,一個辦法里只能有一個這樣的形參。別的,如果這個辦法還有其它的形參,要把它們放到前面的方位上。
編譯器會在背地里把這最終一個形參轉(zhuǎn)化為一個數(shù)組形參,并在編譯出的class文件里作上一個記號,標明這是個實參個數(shù)可變的辦法。
清單2:實參個數(shù)可變的辦法的隱秘形狀

由于存在著這樣的轉(zhuǎn)化,所以不能再為這個類界說一個和轉(zhuǎn)化后的辦法簽名共同的辦法。
清單3:會導致編譯過錯的組合

空白的存亡問題
依據(jù)J2SE 1.5的語法,在“…”前面的空白字符是可有可無的。這樣就有在“…”前面添加空白字符(形如“Object … args”)和在“…”前面不加空白字符(形如“Object… args”)的兩種寫法。由于現(xiàn)在和J2SE 1.5相合作的Java Code Conventions還沒有正式發(fā)布,所以無法知道終究哪一種寫法比較正統(tǒng)。不過,考慮到數(shù)組參數(shù)也有“Object [] args”和“Object[] args”兩種書寫辦法,而正統(tǒng)的寫法是不在“[]”前添加空白字符,好像采納不加空白的“Object… args”的寫法在全體上更和諧一些。
調(diào)用實參個數(shù)可變的辦法
只要把要傳遞的實參逐個寫到相應的方位上,就能夠調(diào)用一個實參個數(shù)可變的辦法。不需求其它的進程。
清單4:能夠傳遞若干個實參

在背地里,編譯器會把這種調(diào)用進程轉(zhuǎn)化為用“數(shù)組包裹實參”的辦法:
清單5:悄悄呈現(xiàn)的數(shù)組創(chuàng)立

別的,這兒說的“不斷定個”也包含零個,所以這樣的調(diào)用也是合乎情理的:
清單6:也能夠傳遞零個實參

清單7:零實參對應空數(shù)組

留意這時傳遞曩昔的是一個空數(shù)組,而不是null。這樣就能夠采納共同的辦法來處理,而不用檢測究竟歸于哪種狀況。
4. 處理個數(shù)可變的實參
處理個數(shù)可變的實參的辦法,和處理數(shù)組實參的辦法根本相同。一切的實參,都被保存到一個和形參同名的數(shù)組里。依據(jù)實踐的需求,把這個數(shù)組里的元素讀出之后,要蒸要煮,就能夠隨意了。
清單8:處理收到的實參們
5. 轉(zhuǎn)發(fā)個數(shù)可變的實參
有時分,在承受了一組個數(shù)可變的實參之后,還要把它們傳遞給另一個實參個數(shù)可變的辦法。由于編碼時無法知道承受來的這一組實參的數(shù)目,所以“把它們 逐個寫到該呈現(xiàn)的方位上去”的做法并不行行。不過,這并不意味著這是個不行完結(jié)的使命,由于還有別的一種辦法,能夠用來調(diào)用實參個數(shù)可變的辦法。
在J2SE 1.5的編譯器的眼中,實參個數(shù)可變的辦法是最終帶了一個數(shù)組形參的辦法的特例。因而,事前把整組要傳遞的實參放到一個數(shù)組里,然后把這個數(shù)組作為最終一個實參,傳遞給一個實參個數(shù)可變的辦法,不會形成任何過錯。憑借這一特性,就能夠順暢的完結(jié)轉(zhuǎn)發(fā)了。
清單9:轉(zhuǎn)發(fā)收到的實參們
Java里的“printf”和“sprintf”
C言語里的printf(按必定的格局輸出字符串)和sprintf(按必定的格局組合字符串)是非常經(jīng)典的運用Varargs機制的比如。在 J2SE 1.5中,也分別在java.io.PrintStream類和java.lang.String類中供給了相似的功用。
按必定的格局輸出字符串的功用,能夠經(jīng)過調(diào)用PrintStream目標的printf(String format, Object… args)辦法來完成。
按必定的格局組合字符串的作業(yè),則能夠經(jīng)過調(diào)用String類的String format(String format, Object… args)靜態(tài)辦法來進行。
6. 是數(shù)組?不是數(shù)組?
雖然在背地里,編譯器會把能匹配不斷定個實參的形參,轉(zhuǎn)化為數(shù)組形參;并且也能夠用數(shù)組包了實參,再傳遞給實參個數(shù)可變的辦法;可是,這并不表明“能匹配不斷定個實參的形參”和“數(shù)組形參”徹底沒有差異。
一個顯著的差異是,如果按照調(diào)用實參個數(shù)可變的辦法的辦法,來調(diào)用一個最終一個形參是數(shù)組形參的辦法,只會導致一個“cannot be applied to”的編譯過錯。
清單10:一個“cannot be applied to”的編譯過錯
由于這一原因,不能在調(diào)用只支撐用數(shù)組包裹實參的辦法的時分(例如在不是專門為J2SE 1.5規(guī)劃第三方類庫中留傳的那些),直接選用這種簡明的調(diào)用辦法。
如果不能修正本來的類,為要調(diào)用的辦法添加參數(shù)個數(shù)可變的版別,而又想選用這種簡明的調(diào)用辦法,那么能夠憑借“引進外加函數(shù)(Introduce Foreign Method)”和“引進本地擴展(Intoduce Local Extension)”的重構辦法來近似的到達意圖。
7. 當個數(shù)可變的實參遇到泛型
J2SE 1.5中新增了“泛型”的機制,能夠在必定條件下把一個類型參數(shù)化。例如,能夠在編寫一個類的時分,把一個辦法的形參的類型用一個標識符(如T)來代表, 至于這個標識符究竟表明什么類型,則在生成這個類的實例的時分再行指定。這一機制能夠用來供給更充沛的代碼重用和更嚴厲的編譯時類型查看。
不過泛型機制卻不能和個數(shù)可變的形參合作運用。如果把一個能和不斷定個實參相匹配的形參的類型,用一個標識符來代表,那么編譯器會給出一個“generic array creation”的過錯。
清單11:當Varargs遇上泛型
形成這個現(xiàn)象的原因在于J2SE 1.5中的泛型機制的一個內(nèi)涵束縛——不能拿用標識符來代表的類型來創(chuàng)立這一類型的實例。在呈現(xiàn)支撐沒有了這個束縛的Java版別之前,關于這個問題,根本沒有太好的解決辦法。
不過,傳統(tǒng)的“用數(shù)組包裹”的做法,并不受這個束縛的約束。
清單12:能夠編譯的變通做法
8. 重載中的挑選問題
Java支撐“重載”的機制,答應在同一個類具有許多只要形參列表不同的辦法。然后,由編譯器依據(jù)調(diào)用時的實參來挑選究竟要履行哪一個辦法。
傳統(tǒng)上的挑選,根本是按照“特別者優(yōu)先”的準則來進行。一個辦法的特別程度,取決于為了讓它順暢運轉(zhuǎn)而需求滿意的條件的數(shù)目,需求條件越多的越特別。
在引進Varargs機制之后,這一準則依然適用,僅僅要考慮的問題豐厚了一些——傳統(tǒng)上,一個重載辦法的各個版別之中,只要形參數(shù)量與實參數(shù)量正 好共同的那些有被進一步考慮的資歷。可是Varargs機制引進之后,徹底能夠呈現(xiàn)兩個版別都能匹配,在其它方面也別無二致,僅僅一個實參個數(shù)固定,而一 個實參個數(shù)可變的狀況。
遇到這種狀況時,所用的斷定規(guī)則是“實參個數(shù)固定的版別優(yōu)先于實參個數(shù)可變的版別”。
清單13:實參個數(shù)固定的版別優(yōu)先
如果在編譯器看來,一起有多個辦法具有相同的優(yōu)先權,它就會墮入無法就究竟調(diào)用哪個辦法作出一個挑選的狀況。在這樣的時分,它就會發(fā)作一個 “reference to 被調(diào)用的辦法名 is ambiguous”的編譯過錯,并耐性的等候作了一些修正,足以革除它的利誘的新源代碼的到來。
在引進了Varargs機制之后,這種可能導致利誘的狀況,又添加了一些。例如現(xiàn)在可能會有兩個版別都能匹配,在其它方面也千篇一律,并且都是實參個數(shù)可變的抵觸發(fā)作。
清單14:左右都不是,為難了編譯器
別的,由于J2SE 1.5中有“Autoboxing/Auto-Unboxing”機制的存在,所以還可能發(fā)作兩個版別都能匹配,并且都是實參個數(shù)可變,其它方面也如出一轍,僅僅一個能承受的實參是根本類型,而另一個能承受的實參是包裹類的抵觸發(fā)作。
清單15:Autoboxing/Auto-Unboxing帶來的新問題
9. 概括總結(jié)
和“用數(shù)組包裹”的做法比較,真實的實參個數(shù)可變的辦法,在調(diào)用時傳遞參數(shù)的操作更為簡略,意義也更為清楚。不過,這一機制也有它本身的限制,并不是一個白璧無瑕的解決方案。