1. 程式人生 > >java中可變長引數的使用方法

java中可變長引數的使用方法

Java中可變長引數的使用及注意事項

1. 可變長引數的定義

使用…表示可變長引數,例如


print(String... args){
...
}

在具有可變長引數的方法中可以把引數當成陣列使用,例如可以迴圈輸出所有的引數值。


print(String... args){
   for(String temp:args)
      System.out.println(temp);
}

2. 可變長引數的方法的呼叫

呼叫的時候可以給出任意多個引數也可不給引數,例如:

print();
print("hello");
print("hello","lisi"
); print("hello","張三", "alexia")

3. 可變長引數的使用規則

3.1 在呼叫方法的時候,如果能夠和固定引數的方法匹配,也能夠與可變長引數的方法匹配,則選擇固定引數的方法。看下面程式碼的輸出:


package com;
// 這裡使用了靜態匯入
import static java.lang.System.out;
public class VarArgsTest {
public void print(String... args) {
for (int i = 0; i < args.length; i++) {
out.println(args[i]);
}
}
public
void print(String test) { out.println("----------"); } public static void main(String[] args) { VarArgsTest test = new VarArgsTest(); test.print("hello"); test.print("hello", "alexia"); } }

這裡寫圖片描述
3.2 如果要呼叫的方法可以和兩個可變引數匹配,則出現錯誤,例如下面的程式碼:


package com;
// 這裡使用了靜態匯入
import static java.lang.System.out;
public
class VarArgsTest1 { public void print(String... args) { for (int i = 0; i < args.length; i++) { out.println(args[i]); } } public void print(String test,String...args ){ out.println("----------"); } public static void main(String[] args) { VarArgsTest1 test = new VarArgsTest1(); test.print("hello"); test.print("hello", "alexia"); } }

對於上面的程式碼,main方法中的兩個呼叫都不能編譯通過,因為編譯器不知道該選哪個方法呼叫,如下所示:
這裡寫圖片描述
3.3 一個方法只能有一個可變長引數,並且這個可變長引數必須是該方法的最後一個引數以下兩種方法定義都是錯誤的。


public void test(String... strings,ArrayList list){
}
public void test(String... strings,ArrayList... list){
}

4. 可變長引數的使用規範

4.1 避免帶有可變長引數的方法過載:如3.1中,編譯器雖然知道怎麼呼叫,但人容易陷入呼叫的陷阱及誤區
4.2 別讓null值和空值威脅到變長方法,如3.2中所示,為了說明null值的呼叫,重新給出一個例子:


package com;public class VarArgsTest1 {
public void print(String test, Integer... is) {
}
public void print(String test,String...args ){
}
public static void main(String[] args) {
VarArgsTest1 test = new VarArgsTest1();
test.print("hello");
test.print("hello", null);
}
}

這時會發現兩個呼叫編譯都不通過:
這裡寫圖片描述
因為兩個方法都匹配,編譯器不知道選哪個,於是報錯了,這裡同時還有個非常不好的編碼習慣,即呼叫者隱藏了實參型別,這是非常危險的,不僅僅呼叫者需要“猜測”該呼叫哪個方法,而且被呼叫者也可能產生內部邏輯混亂的情況。對於本例來說應該做如下修改:

public static void main(String[] args) {
        VarArgsTest1 test = new VarArgsTest1();
        String[] strs = null;
        test.print("hello", strs);
    }

4.3 覆寫變長方法也要循規蹈矩

下面看一個例子,大家猜測下程式能不能編譯通過:

package com;
public class VarArgsTest2 {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
// 向上轉型
Base base = new Sub();
base.print("hello");
// 不轉型
Sub sub = new Sub();
sub.print("hello");
}
}
// 基類
class Base {
void print(String... args) {
System.out.println("Base......test");
}
}
// 子類,覆寫父類方法
class Sub extends Base {
@Override
void print(String[] args) {
System.out.println("Sub......test");
}
}

答案當然是編譯不通過,是不是覺得很奇怪?
這裡寫圖片描述

第一個能編譯通過,這是為什麼呢?事實上,base物件把子類物件sub做了向上轉型,形參列表是由父類決定的,當然能通過。而看看子類直接呼叫的情況,這時編譯器看到子類覆寫了父類的print方法,因此肯定使用子類重新定義的print方法,儘管引數列表不匹配也不會跑到父類再去匹配下,因為找到了就不再找了,因此有了型別不匹配的錯誤。

  這是個特例,覆寫的方法引數列表竟然可以與父類不相同,這違背了覆寫的定義,並且會引發莫名其妙的錯誤。

  這裡,總結下覆寫必須滿足的條件:

  (1)重寫方法不能縮小訪問許可權;

  (2)引數列表必須與被重寫方法相同(包括顯示形式);

  (3)返回型別必須與被重寫方法的相同或是其子類;

  (4)重寫方法不能丟擲新的異常,或者超過了父類範圍的異常,但是可以丟擲更少、更有限的異常,或者不丟擲異常。

 最後,給出一個有陷阱的例子,大家應該知道輸出結果:
 


package com;
public class VarArgsTest {
public static void m1(String s, String... ss) {
for (int i = 0; i < ss.length; i++) {
System.out.println(ss[i]);
}
}
public static void main(String[] args) {
m1("");
m1("aaa");
m1("aaa", "bbb");
}
}