2012-10-14 37 views
20

以下代碼不能編譯。在Java中重載方法的可變參數

package varargspkg; 

public class Main { 

    public static void test(int... i) { 
     for (int t = 0; t < i.length; t++) { 
      System.out.println(i[t]); 
     } 

     System.out.println("int"); 
    } 

    public static void test(float... f) { 
     for (int t = 0; t < f.length; t++) { 
      System.out.println(f[t]); 
     } 

     System.out.println("float"); 
    } 

    public static void main(String[] args) { 
     test(1, 2); //Compilation error here quoted as follows. 
    } 
} 

發出編譯時錯誤。

參考測試是不明確的,在varargspkg.Main匹配在 varargspkg.Main兩個方法測試(INT ...)和方法試驗(浮動...)

這似乎是明顯,因爲在該方法中的參數值調用test(1, 2);可以提升爲int以及float

如果參數中的任何一個或兩者都被Ff後綴,它編譯。


如果我們然而,代表與相應的包裝類型的方法簽名的接收參數如下

public static void test(Integer... i) { 
    System.out.println("Integer" + Arrays.asList(i)); 
} 

public static void test(Float... f) { 
    System.out.println("Float" + Arrays.asList(f)); 
} 

然後調用test(1, 2);不發出任何編譯錯誤的方法。在這種情況下調用的方法是接受一個Integer varargs參數(在前面的代碼片段中的第一個參數)。

爲什麼在這種情況下,第一種情況的錯誤沒有報告?看起來這裏應用了自動裝箱和自動類型提升。首先應用自動裝箱,以便解決錯誤?

甲骨文的文檔說,

一般來說,你不應該重載可變參數的方法,或者 將是困難的程序員找出哪些超載得到 調用。

link的最後一句話。但是爲了更好地理解可變參數。

還要添加下面的代碼編譯就好了。

public class OverLoading { 

    public static void main(String[] args) { 
     load(1); 
    } 

    public static void load(int i) { 
     System.out.println("int"); 
    } 

    public static void load(float i) { 
     System.out.println("float"); 
    } 
} 

編輯:

以下是指示編譯錯誤snap shot。我創建了一個新的應用程序,因此包名稱不同。

enter image description here

我使用的是JDK 6

+3

你原來的代碼適合我。輸出爲 int – techfoobar

+0

@techfoobar - 第一個代碼片段中的代碼無法編譯(使用jdk 6,NetBeans 6.9.1)。它會按照指定生成編譯錯誤。它真的爲你編譯? – Tiny

+0

好問題。想補充說,當我們覆蓋單個參數(int)和(float)時,它會得到解決。我在這裏聞到一個錯誤。 –

回答

9

您可以WidenBox但你不能兩者都做,除非你是boxing and wideningObject(以整數(拳擊),然後整數一個int,對象(擴大)是合法的,因爲每個類是Object一個子類,因此可能的是Integer要傳遞給Object參數)

同樣的intNumber也是合法的(INT - >整數 - >數) 由於號是超類的Integer它是可能的。

讓我們來看看在你的榜樣: -

public static void test(Integer...i) 

public static void test(Float...f) 

有選擇哪個重載的方法來選擇,當拳擊,拓寬和var-ARG遊戲結合時所遵循的一些規則: -

  1. 原始加寬使用smallest方法參數可能
  2. 包裝類型不能被加寬到另一個包裝類型
  3. 你可以從int到Integer並加寬到Object但不到長
  4. 加寬節拍拳擊,拳擊節拍Var-args。
  5. 可以框,然後拓寬(安int可以成爲Object通過Integer
  6. 不能拓寬,然後盒(一個int不能成爲Long
  7. 不能合併VAR-ARGS,有或者是擴大或者拳擊

因此,基於上面給出的規則: -

當你傳遞兩個整數上述功能,

  • 根據規則3,它必須是第一Widened然後 Boxed適應一個Long,其中非法根據規則5(你不能拓寬再盒)是。
  • 所以,它是盒裝存儲在Integer var-args。

但在第一種情況下,你有方法與基本類型的var-args: -

public static void test(int...i) 
public static void test(float...f) 

然後test(1, 2)可以同時使用兩種方法(因爲它們都不是更適合rule 1適用): -

  • 在第一種情況下它將是var-args
  • 在第二種情況下,將被加寬ING然後VAR-ARGS(這是允許的)

現在,當你有正好一個int和一個flost方法: - 然後使用test(1)調用

public static void test(int i) 
public static void test(float f) 

,規則1之後,和儘可能小的擴大(即其中根本不需要加寬的int)被選擇。所以第一種方法將被調用。

欲瞭解更多信息,可以參考JLS - Method Invocation Conversion

+0

@Tiny。我想你應該看看我所解釋的。我認爲這對你很清楚。 :) –

+0

Rohit,準確地說,沒有這樣的東西,例如*將「int」裝入Object的實例*。實際上,當你將一個'int'傳遞給一個接受一個Object的函數時,會發生什麼呢?這個int被自動寫入到Integer的一個實例中,因爲每個類都繼承了,所以它可以被看作是一個Object。來自Object。要驗證這一點,'class x {boolean b(Object o){return o.getClass()== Object.class;} void main(String ... args){System.out.println(b(1)); }}'會打印false。很明顯,轉到'o.getClass()instanceof Object'將顯示爲true。 –

+0

(這就是爲什麼將一個'int'傳遞給一個將'Number'作爲參數的實例 - 'Integer'繼承'Number''的方法也是合法的) –

2

在Java中,1是你如何代表int。它可以自動裝箱到Integer的實例或升級到float,這就解釋了爲什麼編譯器不能決定它應該調用的方法。但它永遠不會被自動裝箱到LongFloat(或任何其他類型)。

在另一方面,如果你寫1F,它是一個float的代表性,即可以自動裝箱爲一個Float(和,本着同樣的精神,將永遠不會自動裝箱到Integer或任何其他)。

2

在Java 6中,問題出在您的泛型的instantiation時間,然後才找到可用的調用方法。

When you write 1,2 
    -> it can be be both int[] or float[] and hence the issue being complained. 

When you write 1,2F 
    -> it can be be only float[] and hence the NO issue being complained. 

同樣的,另外兩個選項,即

When you write 1F,2 
    -> it can be be only float[] and hence the NO issue being complained. 

When you write 1F,2F 
    -> it can be be only float[] and hence the NO issue being complained. 

在另一方面,當您使用intfloat,不存在變量類型實例。當您使用1時,它會首先嚐試使用int作爲參數,如果不是,則會提升類型並使用float標識該方法。如果兩種方法都可用,則首先使用int

由於Java數據類型檢查和提升有更好的處理方式,因此在Java 7中不會出現歧義問題。

+1

用你的邏輯,當我們覆蓋單個參數int和float時,它也應該給出一個錯誤。但它沒有給出任何錯誤。 public class OverLoading { \t public static void main(String [] args){ \t \t load(1); \t} \t \t 公共靜態無效負載(int i)以{ \t \t的System.out.println( 「INT」); \t} \t \t 公共靜態無效負載(浮動ⅰ){ \t \t的System.out.println( 「浮動」); \t} } –

+0

當你重寫單個參數時,例如'1,2F',作爲一個參數作爲嚴格的float,唯一適用的方法是'test(float ..)'而不是'main(int ..)'。這是如何沒有歧義,因此沒有問題。 –

+0

所以你說輸出到示例(現在添加到問題)將輸出浮動? –

2

爲什麼在這種情況下,誤差在第一種情況不報?看起來這裏應用了自動裝箱和自動類型提升。是否首先應用自動裝箱錯誤已解決?

只是一個觀點 - 在可變參數的情況下,JVM實際上必須創建一個參數數組。在Integer和Float的情況下,顯然它應該創建什麼類型的數組。所以,這可能是沒有模棱兩可錯誤的原因。

但仍然有點混亂,爲什麼它不能創建一個整型數組,當默認情況下,1,3是整數。

根據討論,看起來這在SO中已經討論過bug with varargs and overloading?和事實a bug