Java中的可變參數(shù)常見(jiàn)用法實(shí)例總結(jié)
本文實(shí)例講述了Java中的可變參數(shù)常見(jiàn)用法。分享給大家供大家參考,具體如下:
前言:
到J2SE 1.4為止,一直無(wú)法在Java程序里定義實(shí)參個(gè)數(shù)可變的方法——因?yàn)镴ava要求實(shí)參(Arguments)和形參(Parameters)的數(shù)量和類(lèi)型都必須逐一匹配,而形參的數(shù)目是在定義方法時(shí)就已經(jīng)固定下來(lái)了。盡管可以通過(guò)重載機(jī)制,為同一個(gè)方法提供帶有不同數(shù)量的形參的版本,但是這仍然不能達(dá)到讓實(shí)參數(shù)量任意變化的目的。
然而,有些方法的語(yǔ)義要求它們必須能接受個(gè)數(shù)可變的實(shí)參——例如著名的main方法,就需要能接受所有的命令行參數(shù)為實(shí)參,而命令行參數(shù)的數(shù)目,事先根本無(wú)法確定下來(lái)。
對(duì)于這個(gè)問(wèn)題,傳統(tǒng)上一般是采用“利用一個(gè)數(shù)組來(lái)包裹要傳遞的實(shí)參”的做法來(lái)應(yīng)付。
1. 用數(shù)組包裹實(shí)參
“用數(shù)組包裹實(shí)參”的做法可以分成三步:首先,為這個(gè)方法定義一個(gè)數(shù)組型的參數(shù);然后在調(diào)用時(shí),生成一個(gè)包含了所有要傳遞的實(shí)參的數(shù)組;最后,把這個(gè)數(shù)組作為一個(gè)實(shí)參傳遞過(guò)去。
這種做法可以有效的達(dá)到“讓方法可以接受個(gè)數(shù)可變的參數(shù)”的目的,只是調(diào)用時(shí)的形式不夠簡(jiǎn)單。
J2SE 1.5中提供了Varargs機(jī)制,允許直接定義能和多個(gè)實(shí)參相匹配的形參。從而,可以用一種更簡(jiǎn)單的方式,來(lái)傳遞個(gè)數(shù)可變的實(shí)參。
Varargs的含義
大體說(shuō)來(lái),“Varargs”是“variable number of arguments”的意思。有時(shí)候也被簡(jiǎn)單的稱(chēng)為“variable arguments”,不過(guò)因?yàn)檫@一種叫法沒(méi)有說(shuō)明是什么東西可變,所以意義稍微有點(diǎn)模糊。
2. 定義實(shí)參個(gè)數(shù)可變的方法
只要在一個(gè)形參的“類(lèi)型”與“參數(shù)名”之間加上三個(gè)連續(xù)的“.”(即“...”,英文里的句中省略號(hào)),就可以讓它和不確定個(gè)實(shí)參相匹配。而一個(gè)帶有這樣的形參的方法,就是一個(gè)實(shí)參個(gè)數(shù)可變的方法。
- 清單1:一個(gè)實(shí)參個(gè)數(shù)可變的方法
private static int sumUp(int... values) { }
注意,只有最后一個(gè)形參才能被定義成“能和不確定個(gè)實(shí)參相匹配”的。因此,一個(gè)方法里只能有一個(gè)這樣的形參。另外,如果這個(gè)方法還有其它的形參,要把它們放到前面的位置上。
編譯器會(huì)在背地里把這最后一個(gè)形參轉(zhuǎn)化為一個(gè)數(shù)組形參,并在編譯出的class文件里作上一個(gè)記號(hào),表明這是個(gè)實(shí)參個(gè)數(shù)可變的方法。
- 清單2:實(shí)參個(gè)數(shù)可變的方法的秘密形態(tài)
private static int sumUp(int[] values) { }
由于存在著這樣的轉(zhuǎn)化,所以不能再為這個(gè)類(lèi)定義一個(gè)和轉(zhuǎn)化后的方法簽名一致的方法。
- 清單3:會(huì)導(dǎo)致編譯錯(cuò)誤的組合
private static int sumUp(int... values) { } private static int sumUp(int[] values) { }
3. 調(diào)用實(shí)參個(gè)數(shù)可變的方法
只要把要傳遞的實(shí)參逐一寫(xiě)到相應(yīng)的位置上,就可以調(diào)用一個(gè)實(shí)參個(gè)數(shù)可變的方法。不需要其它的步驟。
- 清單4:可以傳遞若干個(gè)實(shí)參
sumUp(1, 3, 5, 7);
在背地里,編譯器會(huì)把這種調(diào)用過(guò)程轉(zhuǎn)化為用“數(shù)組包裹實(shí)參”的形式:
- 清單5:偷偷出現(xiàn)的數(shù)組創(chuàng)建
sumUp(new int[]{1, 2, 3, 4});
另外,這里說(shuō)的“不確定個(gè)”也包括零個(gè),所以這樣的調(diào)用也是合乎情理的:
- 清單6:也可以傳遞零個(gè)實(shí)參
sumUp();
這種調(diào)用方法被編譯器秘密轉(zhuǎn)化之后的效果,則等同于這樣:
- 清單7:零實(shí)參對(duì)應(yīng)空數(shù)組
sumUp(new int[]{});
注意這時(shí)傳遞過(guò)去的是一個(gè)空數(shù)組,而不是null。這樣就可以采取統(tǒng)一的形式來(lái)處理,而不必檢測(cè)到底屬于哪種情況。
4. 處理個(gè)數(shù)可變的實(shí)參
處理個(gè)數(shù)可變的實(shí)參的辦法,和處理數(shù)組實(shí)參的辦法基本相同。所有的實(shí)參,都被保存到一個(gè)和形參同名的數(shù)組里。根據(jù)實(shí)際的需要,把這個(gè)數(shù)組里的元素讀出之后,要蒸要煮,就可以隨意了。
- 清單8:處理收到的實(shí)參們
private static int sumUp(int... values) { int sum = 0; for (int i = 0; i < values.length; i++) { sum += values[i]; } return sum; }
5. 轉(zhuǎn)發(fā)個(gè)數(shù)可變的實(shí)參
有時(shí)候,在接受了一組個(gè)數(shù)可變的實(shí)參之后,還要把它們傳遞給另一個(gè)實(shí)參個(gè)數(shù)可變的方法。因?yàn)榫幋a時(shí)無(wú)法知道接受來(lái)的這一組實(shí)參的數(shù)目,所以“把它們逐一寫(xiě)到該出現(xiàn)的位置上去”的做法并不可行。不過(guò),這并不意味著這是個(gè)不可完成的任務(wù),因?yàn)檫€有另外一種辦法,可以用來(lái)調(diào)用實(shí)參個(gè)數(shù)可變的方法。
在J2SE 1.5的編譯器的眼中,實(shí)參個(gè)數(shù)可變的方法是最后帶了一個(gè)數(shù)組形參的方法的特例。因此,事先把整組要傳遞的實(shí)參放到一個(gè)數(shù)組里,然后把這個(gè)數(shù)組作為最后一個(gè)實(shí)參,傳遞給一個(gè)實(shí)參個(gè)數(shù)可變的方法,不會(huì)造成任何錯(cuò)誤。借助這一特性,就可以順利的完成轉(zhuǎn)發(fā)了。
- 清單9:轉(zhuǎn)發(fā)收到的實(shí)參們
public class PrintfSample { public static void main(String[] args) { printOut("Pi:%f E:%f\n", Math.PI, Math.E); } private static void printOut(String format, Object... args) { System.out.printf(format, args); } }
6. 是數(shù)組?不是數(shù)組?
盡管在背地里,編譯器會(huì)把能匹配不確定個(gè)實(shí)參的形參,轉(zhuǎn)化為數(shù)組形參;而且也可以用數(shù)組包了實(shí)參,再傳遞給實(shí)參個(gè)數(shù)可變的方法;但是,這并不表示“能匹配不確定個(gè)實(shí)參的形參”和“數(shù)組形參”完全沒(méi)有差異。
一個(gè)明顯的差異是,如果按照調(diào)用實(shí)參個(gè)數(shù)可變的方法的形式,來(lái)調(diào)用一個(gè)最后一個(gè)形參是數(shù)組形參的方法,只會(huì)導(dǎo)致一個(gè)“cannot be applied to”的編譯錯(cuò)誤。
- 清單10:一個(gè)“cannot be applied to”的編譯錯(cuò)誤
private static void testOverloading(int[] i) { System.out.println("A"); } public static void main(String[] args) { testOverloading(1, 2, 3);//編譯出錯(cuò) }
由于這一原因,不能在調(diào)用只支持用數(shù)組包裹實(shí)參的方法的時(shí)候(例如在不是專(zhuān)門(mén)為J2SE 1.5設(shè)計(jì)第三方類(lèi)庫(kù)中遺留的那些),直接采用這種簡(jiǎn)明的調(diào)用方式。
如果不能修改原來(lái)的類(lèi),為要調(diào)用的方法增加參數(shù)個(gè)數(shù)可變的版本,而又想采用這種簡(jiǎn)明的調(diào)用方式,那么可以借助“引入外加函數(shù)(Introduce Foreign Method)”和“引入本地?cái)U(kuò)展(Intoduce Local Extension)”的重構(gòu)手法來(lái)近似的達(dá)到目的。
7. 當(dāng)個(gè)數(shù)可變的實(shí)參遇到泛型
J2SE 1.5中新增了“泛型”的機(jī)制,可以在一定條件下把一個(gè)類(lèi)型參數(shù)化。例如,可以在編寫(xiě)一個(gè)類(lèi)的時(shí)候,把一個(gè)方法的形參的類(lèi)型用一個(gè)標(biāo)識(shí)符(如T)來(lái)代表,至于這個(gè)標(biāo)識(shí)符到底表示什么類(lèi)型,則在生成這個(gè)類(lèi)的實(shí)例的時(shí)候再行指定。這一機(jī)制可以用來(lái)提供更充分的代碼重用和更嚴(yán)格的編譯時(shí)類(lèi)型檢查。
不過(guò)泛型機(jī)制卻不能和個(gè)數(shù)可變的形參配合使用。如果把一個(gè)能和不確定個(gè)實(shí)參相匹配的形參的類(lèi)型,用一個(gè)標(biāo)識(shí)符來(lái)代表,那么編譯器會(huì)給出一個(gè)“generic array creation”的錯(cuò)誤。
- 清單11:當(dāng)Varargs遇上泛型
private static void testVarargs(T... args) {//編譯出錯(cuò) }
造成這個(gè)現(xiàn)象的原因在于J2SE 1.5中的泛型機(jī)制的一個(gè)內(nèi)在約束——不能拿用標(biāo)識(shí)符來(lái)代表的類(lèi)型來(lái)創(chuàng)建這一類(lèi)型的實(shí)例。在出現(xiàn)支持沒(méi)有了這個(gè)約束的Java版本之前,對(duì)于這個(gè)問(wèn)題,基本沒(méi)有太好的解決辦法。
不過(guò),傳統(tǒng)的“用數(shù)組包裹”的做法,并不受這個(gè)約束的限制。
- 清單12:可以編譯的變通做法
private static void testVarargs(T[] args) { for (int i = 0; i < args.length; i++) { System.out.println(args[i]); } }
8. 重載中的選擇問(wèn)題
Java支持“重載”的機(jī)制,允許在同一個(gè)類(lèi)擁有許多只有形參列表不同的方法。然后,由編譯器根據(jù)調(diào)用時(shí)的實(shí)參來(lái)選擇到底要執(zhí)行哪一個(gè)方法。
傳統(tǒng)上的選擇,基本是依照“特殊者優(yōu)先”的原則來(lái)進(jìn)行。一個(gè)方法的特殊程度,取決于為了讓它順利運(yùn)行而需要滿足的條件的數(shù)目,需要條件越多的越特殊。
在引入Varargs機(jī)制之后,這一原則仍然適用,只是要考慮的問(wèn)題豐富了一些——傳統(tǒng)上,一個(gè)重載方法的各個(gè)版本之中,只有形參數(shù)量與實(shí)參數(shù)量正好一致的那些有被進(jìn)一步考慮的資格。但是Varargs機(jī)制引入之后,完全可以出現(xiàn)兩個(gè)版本都能匹配,在其它方面也別無(wú)二致,只是一個(gè)實(shí)參個(gè)數(shù)固定,而一個(gè)實(shí)參個(gè)數(shù)可變的情況。
遇到這種情況時(shí),所用的判定規(guī)則是“實(shí)參個(gè)數(shù)固定的版本優(yōu)先于實(shí)參個(gè)數(shù)可變的版本”。
- 清單13:實(shí)參個(gè)數(shù)固定的版本優(yōu)先
如果在編譯器看來(lái),同時(shí)有多個(gè)方法具有相同的優(yōu)先權(quán),它就會(huì)陷入無(wú)法就到底調(diào)用哪個(gè)方法作出一個(gè)選擇的狀態(tài)。在這樣的時(shí)候,它就會(huì)產(chǎn)生一個(gè)“reference to 被調(diào)用的方法名 is ambiguous”的編譯錯(cuò)誤,并耐心的等候作了一些修改,足以免除它的迷惑的新源代碼的到來(lái)。
在引入了Varargs機(jī)制之后,這種可能導(dǎo)致迷惑的情況,又增加了一些。例如現(xiàn)在可能會(huì)有兩個(gè)版本都能匹配,在其它方面也如出一轍,而且都是實(shí)參個(gè)數(shù)可變的沖突發(fā)生。
public class OverloadingSampleA { public static void main(String[] args) { testOverloading(1);//打印出A testOverloading(1, 2);//打印出B testOverloading(1, 2, 3);//打印出C } private static void testOverloading(int i) { System.out.println("A"); } private static void testOverloading(int i, int j) { System.out.println("B"); } private static void testOverloading(int i, int... more) { System.out.println("C"); } }
如果在編譯器看來(lái),同時(shí)有多個(gè)方法具有相同的優(yōu)先權(quán),它就會(huì)陷入無(wú)法就到底調(diào)用哪個(gè)方法作出一個(gè)選擇的狀態(tài)。在這樣的時(shí)候,它就會(huì)產(chǎn)生一個(gè)“reference to 被調(diào)用的方法名 is ambiguous”的編譯錯(cuò)誤,并耐心的等候作了一些修改,足以免除它的迷惑的新源代碼的到來(lái)。
在引入了Varargs機(jī)制之后,這種可能導(dǎo)致迷惑的情況,又增加了一些。例如現(xiàn)在可能會(huì)有兩個(gè)版本都能匹配,在其它方面也如出一轍,而且都是實(shí)參個(gè)數(shù)可變的沖突發(fā)生。
- 清單14:左右都不是,為難了編譯器
public class OverloadingSampleB { public static void main(String[] args) { testOverloading(1, 2, 3);//編譯出錯(cuò) } private static void testOverloading(Object... args) { } private static void testOverloading(Object o, Object... args) { } }
另外,因?yàn)镴2SE 1.5中有“Autoboxing/Auto-Unboxing”機(jī)制的存在,所以還可能發(fā)生兩個(gè)版本都能匹配,而且都是實(shí)參個(gè)數(shù)可變,其它方面也一模一樣,只是一個(gè)能接受的實(shí)參是基本類(lèi)型,而另一個(gè)能接受的實(shí)參是包裹類(lèi)的沖突發(fā)生。
- 清單15:Autoboxing/Auto-Unboxing帶來(lái)的新問(wèn)題
public class OverloadingSampleC { public static void main(String[] args) { /* 編譯出錯(cuò) */ testOverloading(1, 2); /* 還是編譯出錯(cuò) */ testOverloading(new Integer(1), new Integer(2)); } private static void testOverloading(int... args) { } private static void testOverloading(Integer... args) { } }
9. 歸納總結(jié)
和“用數(shù)組包裹”的做法相比,真正的實(shí)參個(gè)數(shù)可變的方法,在調(diào)用時(shí)傳遞參數(shù)的操作更為簡(jiǎn)單,含義也更為清楚。不過(guò),這一機(jī)制也有它自身的局限,并不是一個(gè)完美無(wú)缺的解決方案。
更多java相關(guān)內(nèi)容感興趣的讀者可查看本站專(zhuān)題:《Java面向?qū)ο蟪绦蛟O(shè)計(jì)入門(mén)與進(jìn)階教程》、《Java數(shù)據(jù)結(jié)構(gòu)與算法教程》、《Java操作DOM節(jié)點(diǎn)技巧總結(jié)》、《Java文件與目錄操作技巧匯總》和《Java緩存操作技巧匯總》
希望本文所述對(duì)大家java程序設(shè)計(jì)有所幫助。
相關(guān)文章
基于eclipse.ini內(nèi)存設(shè)置的問(wèn)題詳解
本篇文章是對(duì)eclipse.ini內(nèi)存設(shè)置的問(wèn)題進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05Java Clone(類(lèi)的復(fù)制)實(shí)例代碼
Java Clone(類(lèi)的復(fù)制)實(shí)例代碼,需要的朋友可以參考一下2013-03-03Java實(shí)現(xiàn)短信發(fā)送驗(yàn)證碼功能
這篇文章主要介紹了Java實(shí)現(xiàn)短信發(fā)送驗(yàn)證碼功能,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2018-10-10Java基礎(chǔ)知識(shí)精通數(shù)組的使用
數(shù)組對(duì)于每一門(mén)編程語(yǔ)言來(lái)說(shuō)都是重要的數(shù)據(jù)結(jié)構(gòu)之一,當(dāng)然不同語(yǔ)言對(duì)數(shù)組的實(shí)現(xiàn)及處理也不盡相同。Java?語(yǔ)言中提供的數(shù)組是用來(lái)存儲(chǔ)固定大小的同類(lèi)型元素2022-04-04java實(shí)現(xiàn)自動(dòng)回復(fù)聊天機(jī)器人
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)自動(dòng)回復(fù)聊天機(jī)器人,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-08-08spring boot加載第三方j(luò)ar包的配置文件的方法
本篇文章主要介紹了spring boot加載第三方j(luò)ar包的配置文件的方法,詳細(xì)的介紹了spring boot jar包配置文件的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-10-10Java?Chassis3熔斷機(jī)制的改進(jìn)路程技術(shù)解密
這篇文章主要介紹了Java?Chassis?3技術(shù)解密之熔斷機(jī)制的改進(jìn)路程實(shí)例分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01