大战熟女丰满人妻av-荡女精品导航-岛国aaaa级午夜福利片-岛国av动作片在线观看-岛国av无码免费无禁网站-岛国大片激情做爰视频

Java中Varargs機制的理解


  J2SE1.5提供了“Varargs”機制。借助這一機制,可以定義能和多個實參相匹配的形參。從而,可以用一種更簡單的方式,來傳遞個數(shù)可變的實參。本文介紹這一機制的使用方法,以及這一機制與數(shù)組、泛型、重載之間的相互作用時的若干問題。


  到J2SE1.4為止,一直無法在Java程序里定義實參個數(shù)可變的方法——因為Java要求實參(Arguments)和形參(Parameters)的數(shù)量和類型都必須逐一匹配,而形參的數(shù)目是在定義方法時就已經(jīng)固定下來了。盡管可以通過重載機制,為同一個方法提供帶有不同數(shù)量的形參的版本,但是這仍然不能達到讓實參數(shù)量任意變化的目的。


  然而,有些方法的語義要求它們必須能接受個數(shù)可變的實參——例如著名的main方法,就需要能接受所有的命令行參數(shù)為實參,而命令行參數(shù)的數(shù)目,事先根本無法確定下來。


  對于這個問題,傳統(tǒng)上一般是采用“利用一個數(shù)組來包裹要傳遞的實參”的做法來應付。


  用數(shù)組包裹實參


  “用數(shù)組包裹實參”的做法可以分成三步:首先,為這個方法定義一個數(shù)組型的參數(shù);然后在調(diào)用時,生成一個包含了所有要傳遞的實參的數(shù)組;最后,把這個數(shù)組作為一個實參傳遞過去。


  這種做法可以有效的達到“讓方法可以接受個數(shù)可變的參數(shù)”的目的,只是調(diào)用時的形式不夠簡單。


  J2SE1.5中提供了Varargs機制,允許直接定義能和多個實參相匹配的形參。從而,可以用一種更簡單的方式,來傳遞個數(shù)可變的實參。


  Varargs的含義


  大體說來,“Varargs”是“variablenumberofarguments”的意思。有時候也被簡單的稱為“variablearguments”,不過因為這一種叫法沒有說明是什么東西可變,所以意義稍微有點模糊。


  定義實參個數(shù)可變的方法


  只要在一個形參的“類型”與“參數(shù)名”之間加上三個連續(xù)的“.”(即“…”,英文里的句中省略號),就可以讓它和不確定個實參相匹配。而一個帶有這樣的形參的方法,就是一個實參個數(shù)可變的方法。


  清單1:一個實參個數(shù)可變的方法


  privatestaticintsumUp(int...values){


  }


  注意,只有最后一個形參才能被定義成“能和不確定個實參相匹配”的。因此,一個方法里只能有一個這樣的形參。另外,如果這個方法還有其它的形參,要把它們放到前面的位置上。


  編譯器會在背地里把這最后一個形參轉(zhuǎn)化為一個數(shù)組形參,并在編譯出的class文件里作上一個記號,表明這是個實參個數(shù)可變的方法。


  清單2:實參個數(shù)可變的方法的秘密形態(tài)


  privatestaticintsumUp(int[]values){


  }


  由于存在著這樣的轉(zhuǎn)化,所以不能再為這個類定義一個和轉(zhuǎn)化后的方法簽名一致的方法。


  清單3:會導致編譯錯誤的組合


  privatestaticintsumUp(int...values){


  }


  privatestaticintsumUp(int[]values){


  }


  空白的存亡問題


  根據(jù)J2SE1.5的語法,在“…”前面的空白字符是可有可無的。這樣就有在“…”前面添加空白字符(形如“Object…args”)和在“…”前面不加空白字符(形如“Object…args”)的兩種寫法。因為目前和J2SE1.5相配合的JavaCodeConventions還沒有正式發(fā)布,所以無法知道究竟哪一種寫法比較正統(tǒng)。不過,考慮到數(shù)組參數(shù)也有“Object[]args”和“Object[]args”兩種書寫方式,而正統(tǒng)的寫法是不在“[]”前添加空白字符,似乎采取不加空白的“Object…args”的寫法在整體上更協(xié)調(diào)一些。


  調(diào)用實參個數(shù)可變的方法


  只要把要傳遞的實參逐一寫到相應的位置上,就可以調(diào)用一個實參個數(shù)可變的方法。不需要其它的步驟。


  清單4:可以傳遞若干個實參


  sumUp(1,3,5,7);


  在背地里,編譯器會把這種調(diào)用過程轉(zhuǎn)化為用“數(shù)組包裹實參”的形式:


  清單5:偷偷出現(xiàn)的數(shù)組創(chuàng)建


  sumUp(newint[]{1,2,3,4});


  另外,這里說的“不確定個”也包括零個,所以這樣的調(diào)用也是合乎情理的:


  清單6:也可以傳遞零個實參


  sumUp();


  這種調(diào)用方法被編譯器秘密轉(zhuǎn)化之后的效果,則等同于這樣:


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


  sumUp(newint[]{});


  注意這時傳遞過去的是一個空數(shù)組,而不是null。這樣就可以采取統(tǒng)一的形式來處理,而不必檢測到底屬于哪種情況。


  4.處理個數(shù)可變的實參


  處理個數(shù)可變的實參的辦法,和處理數(shù)組實參的辦法基本相同。所有的實參,都被保存到一個和形參同名的數(shù)組里。根據(jù)實際的需要,把這個數(shù)組里的元素讀出之后,要蒸要煮,就可以隨意了。


  清單8:處理收到的實參們


  privatestaticintsumUp(int...values){


  intsum=0;


  for(inti=0;i<values.length;i++){


  sum+=values[i];


  }


  returnsum;


  }


  5.轉(zhuǎn)發(fā)個數(shù)可變的實參


  有時候,在接受了一組個數(shù)可變的實參之后,還要把它們傳遞給另一個實參個數(shù)可變的方法。因為編碼時無法知道接受來的這一組實參的數(shù)目,所以“把它們逐一寫到該出現(xiàn)的位置上去”的做法并不可行。不過,這并不意味著這是個不可完成的任務,因為還有另外一種辦法,可以用來調(diào)用實參個數(shù)可變的方法。


  在J2SE1.5的編譯器的眼中,實參個數(shù)可變的方法是最后帶了一個數(shù)組形參的方法的特例。因此,事先把整組要傳遞的實參放到一個數(shù)組里,然后把這個數(shù)組作為最后一個實參,傳遞給一個實參個數(shù)可變的方法,不會造成任何錯誤。借助這一特性,就可以順利的完成轉(zhuǎn)發(fā)了。


  清單9:轉(zhuǎn)發(fā)收到的實參們


  publicclassPrintfSample{


  publicstaticvoidmain(String[]args){


  //打印出“Pi:3.141593E:2.718282”


  printOut("Pi:%fE:%f/n",Math.PI,Math.E);


  }


  privatestaticvoidprintOut(Stringformat,Object...args){


  //J2SE1.5里PrintStream新增的printf(Stringformat,Object...args)方法


  System.out.printf(format,args);


  }


  }


  Java里的“printf”和“sprintf”


  C語言里的printf(按一定的格式輸出字符串)和sprintf(按一定的格式組合字符串)是十分經(jīng)典的使用Varargs機制的例子。在J2SE1.5中,也分別在java.io.PrintStream類和java.lang.String類中提供了類似的功能。


  按一定的格式輸出字符串的功能,可以通過調(diào)用PrintStream對象的printf(Stringformat,Object…args)方法來實現(xiàn)。


  按一定的格式組合字符串的工作,則可以通過調(diào)用String類的Stringformat(Stringformat,Object…args)靜態(tài)方法來進行。


  6.是數(shù)組?不是數(shù)組?


  盡管在背地里,編譯器會把能匹配不確定個實參的形參,轉(zhuǎn)化為數(shù)組形參;而且也可以用數(shù)組包了實參,再傳遞給實參個數(shù)可變的方法;但是,這并不表示“能匹配不確定個實參的形參”和“數(shù)組形參”完全沒有差異。


  一個明顯的差異是,如果按照調(diào)用實參個數(shù)可變的方法的形式,來調(diào)用一個最后一個形參是數(shù)組形參的方法,只會導致一個“cannotbeappliedto”的編譯錯誤。


  清單10:一個“cannotbeappliedto”的編譯錯誤


  privatestaticvoidtestOverloading(int[]i){


  System.out.println("A");


  }


  publicstaticvoidmain(String[]args){


  testOverloading(1,2,3);//編譯出錯


  }


  由于這一原因,不能在調(diào)用只支持用數(shù)組包裹實參的方法的時候(例如在不是專門為J2SE1.5設(shè)計第三方類庫中遺留的那些),直接采用這種簡明的調(diào)用方式。


  如果不能修改原來的類,為要調(diào)用的方法增加參數(shù)個數(shù)可變的版本,而又想采用這種簡明的調(diào)用方式,那么可以借助“引入外加函數(shù)(IntroduceForeignMethod)”和“引入本地擴展(IntoduceLocalExtension)”的重構(gòu)手法來近似的達到目的。


  7.當個數(shù)可變的實參遇到泛型


  J2SE1.5中新增了“泛型”的機制,可以在一定條件下把一個類型參數(shù)化。例如,可以在編寫一個類的時候,把一個方法的形參的類型用一個標識符(如T)來代表,至于這個標識符到底表示什么類型,則在生成這個類的實例的時候再行指定。這一機制可以用來提供更充分的代碼重用和更嚴格的編譯時類型檢查。


  不過泛型機制卻不能和個數(shù)可變的形參配合使用。如果把一個能和不確定個實參相匹配的形參的類型,用一個標識符來代表,那么編譯器會給出一個“genericarraycreation”的錯誤。


  清單11:當Varargs遇上泛型


  privatestatic<T>voidtestVarargs(T...args){


  //編譯出錯


  }


  造成這個現(xiàn)象的原因在于J2SE1.5中的泛型機制的一個內(nèi)在約束——不能拿用標識符來代表的類型來創(chuàng)建這一類型的實例。在出現(xiàn)支持沒有了這個約束的Java版本之前,對于這個問題,基本沒有太好的解決辦法。


  不過,傳統(tǒng)的“用數(shù)組包裹”的做法,并不受這個約束的限制。


  清單12:可以編譯的變通做法


  privatestatic<T>voidtestVarargs(T[]args){


  for(inti=0;i<args.length;i++){


  System.out.println(args[i]);


  }


  }


  8.重載中的選擇問題


  Java支持“重載”的機制,允許在同一個類擁有許多只有形參列表不同的方法。然后,由編譯器根據(jù)調(diào)用時的實參來選擇到底要執(zhí)行哪一個方法。


  傳統(tǒng)上的選擇,基本是依照“特殊者優(yōu)先”的原則來進行。一個方法的特殊程度,取決于為了讓它順利運行而需要滿足的條件的數(shù)目,需要條件越多的越特殊。


  在引入Varargs機制之后,這一原則仍然適用,只是要考慮的問題豐富了一些——傳統(tǒng)上,一個重載方法的各個版本之中,只有形參數(shù)量與實參數(shù)量正好一致的那些有被進一步考慮的資格。但是Varargs機制引入之后,完全可以出現(xiàn)兩個版本都能匹配,在其它方面也別無二致,只是一個實參個數(shù)固定,而一個實參個數(shù)可變的情況。


  遇到這種情況時,所用的判定規(guī)則是“實參個數(shù)固定的版本優(yōu)先于實參個數(shù)可變的版本”。


  清單13:實參個數(shù)固定的版本優(yōu)先


  publicclassOverloadingSampleA{


  publicstaticvoidmain(String[]args){


  testOverloading(1);//打印出A


  testOverloading(1,2);//打印出B


  testOverloading(1,2,3);//打印出C


  }


  privatestaticvoidtestOverloading(inti){


  System.out.println("A");


  }


  privatestaticvoidtestOverloading(inti,intj){


  System.out.println("B");


  }


  privatestaticvoidtestOverloading(inti,int...more){


  System.out.println("C");


  }


  }


  如果在編譯器看來,同時有多個方法具有相同的優(yōu)先權(quán),它就會陷入無法就到底調(diào)用哪個方法作出一個選擇的狀態(tài)。在這樣的時候,它就會產(chǎn)生一個“referenceto被調(diào)用的方法名isambiguous”的編譯錯誤,并耐心的等候作了一些修改,足以免除它的迷惑的新源代碼的到來。


  在引入了Varargs機制之后,這種可能導致迷惑的情況,又增加了一些。例如現(xiàn)在可能會有兩個版本都能匹配,在其它方面也如出一轍,而且都是實參個數(shù)可變的沖突發(fā)生。


  清單14:左右都不是,為難了編譯器


  publicclassOverloadingSampleB{


  publicstaticvoidmain(String[]args){


  testOverloading(1,2,3);//編譯出錯


  }


  privatestaticvoidtestOverloading(Object...args){


  }


  privatestaticvoidtestOverloading(Objecto,Object...args){


  }


  }


  另外,因為J2SE1.5中有“Autoboxing/Auto-Unboxing”機制的存在,所以還可能發(fā)生兩個版本都能匹配,而且都是實參個數(shù)可變,其它方面也一模一樣,只是一個能接受的實參是基本類型,而另一個能接受的實參是包裹類的沖突發(fā)生。


  清單15:Autoboxing/Auto-Unboxing帶來的新問題


  publicclassOverloadingSampleC{


  publicstaticvoidmain(String[]args){/*編譯出錯*/


  testOverloading(1,2);/*還是編譯出錯*/


  testOverloading(newInteger(1),newInteger(2));


  }


  privatestaticvoidtestOverloading(int...args){


  }


  privatestaticvoidtestOverloading(Integer...args){


  }


  }


  9.歸納總結(jié)


  和“用數(shù)組包裹”的做法相比,真正的實參個數(shù)可變的方法,在調(diào)用時傳遞參數(shù)的操作更為簡單,含義也更為清楚。不過,這一機制也有它自身的局限,并不是一個完美無缺的解決方案。


  


上一篇:10個高級Java面試題
下一篇:一道有趣的Java面試題

開班信息

主站蜘蛛池模板: 一级毛片aa高清免费观看 | 伊人亚洲 | 国产精品久久久久国产精品 | 久久er99| 亚洲精品国产不卡在线观看 | 高清影院|精品秒播3 | 在线观看日本一区 | 狠狠一区 | 97色伦图片97色伦图影院久久 | 香蕉久久综合精品首页 | 99热这里只有精品88 | 御姐色网| 久久精品国产一区 | 伊人天天干 | 国产成人在线观看免费网站 | 四虎4hu影库永久地址 | 久久99精品久久久久久野外 | 久久精品国产99久久72 | 韩国日本一级毛片免费视频 | 亚洲国产成人精品一区二区三区 | 99re免费视频精品全部 | 一本一本久久a久久综合精品蜜桃 | 久久99综合国产精品亚洲首页 | 免费毛片播放 | 99精品久久99久久久久 | 狠狠色欧美亚洲狠狠色五 | 91在线短视频 | 99久久免费费视频在线观看 | 91手机在线 | 四虎影永久地址www 四虎影永久在线高清免费 四虎影永久在线观看精品 四虎影永久在线观看网址 四虎影院.com | 九九视频只有精品 | 久久精品国产精品2020 | 国产女人成人精品视频 | 日本精品一区二区三本中文 | 色综合亚洲综合网站综合色 | 亚洲a成人7777777久久 | 国产情精品嫩草影院88av | 久久久影院亚洲精品 | 中文字幕 亚洲一区 | 国产一区二区三区在线观看精品 | 欧美视频成人 |