2013-01-09 68 views
136

我最近遇到了java @SafeVarargs註釋。谷歌搜索是什麼使Java的不安全一個可變參數函數留給我搞糊塗了(?堆中毒抹去類型?),所以我想知道的幾件事情:Java SafeVarargs註釋,是否存在標準或最佳實踐?

  1. 是什麼讓在一個可變參數的Java功能不安全@SafeVarargs感(最好以深入的例子的形式解釋)?

  2. 爲什麼此註釋留給程序員的判斷?這不是編譯器應該能夠檢查的東西嗎?

  3. 是否有一些標準必須堅持以確保他的功能確實是安全的?如果沒有,確保它的最佳實踐是什麼?

+1

您是否看過[JavaDoc](http://docs.oracle.com/javase/7/docs/api/java/lang/SafeVarargs.html)中的示例(和解釋)? – jlordo

+2

對於第三個問題,一種做法是始終有第一個元素和其他元素:'retType myMethod(Arg first,Arg ... others)'。如果你沒有指定'first',那麼允許一個空的數組,並且你可能會有一個相同名字的方法,並且沒有參數的返回類型相同,這意味着JVM很難確定哪個方法應該是調用。 – fge

+4

@jlordo我做了,但我不明白爲什麼它在可變參數環境中給出,因爲它可以很容易地補充可變參數函數之外的這種情況(驗證了這一點,在預期的類型安全警告和運行時錯誤編譯)。 – Oren

回答

189

1)在互聯網和StackOverflow上有很多關於泛型和可變參數問題的例子。基本上,當你有一個可變數目的類型參數類型的參數是:

void foo(T... args); 

在Java中,可變參數是一個語法糖,在編譯時經歷了一個簡單的「重新書寫」:一個可變參數的參數類型X...被轉換成類型爲X[]的參數;並且每次調用此可變參數方法時,編譯器都會收集varargs參數中的所有「可變參數」,並創建一個與new X[] { ...(arguments go here)... }類似的數組。

當可變參數類型像String...這樣具體混凝土時,這很有效。當它是一個像T...這樣的類型變量時,它在T被認爲是該調用的具體類型時也可以使用。例如如果上面的方法是類別Foo<T>的一部分,並且您有Foo<String>參考,那麼調用foo就可以了,因爲我們知道T在代碼中的那一點是String

但是,當T的「值」是另一個類型參數時,它不起作用。在Java中,不可能創建一個類型參數組件類型的數組(new T[] { ... })。因此,Java改爲使用new Object[] { ... }(這裏ObjectT的上限;如果上限有些不同,則將代替Object),然後給出編譯器警告。

那麼,創建new Object[]而不是new T[]或什麼是什麼錯?那麼,Java中的數組在運行時就知道它們的組件類型。因此,傳遞的數組對象在運行時將具有錯誤的組件類型。

對於可能是最常見的使用可變參數,只需遍歷的元素,這是沒有問題的(你不關心運行時類型的數組),所以這是安全的:

@SafeVarargs 
final void foo(T... args) { 
    for (T x : args) { 
     // do stuff with x 
    } 
} 

但是,對於依賴於傳遞數組的運行時組件類型的任何事情來說,它將不安全。下面是一些不安全和崩潰一個簡單的例子:

class UnSafeVarargs 
{ 
    static <T> T[] asArray(T... args) { 
    return args; 
    } 

    static <T> T[] arrayOfTwo(T a, T b) { 
    return asArray(a, b); 
    } 

    public static void main(String[] args) { 
    String[] bar = arrayOfTwo("hi", "mom"); 
    } 
} 

這裏的問題是,我們依賴於args類型是爲了返回它T[]T[]。但實際上,運行時參數的類型不是T[]的實例。

3)如果您的方法有T...類型(其中,T是任何類型的參數的一個參數),則:

  • 安全:如果你的方法只依賴於一個事實,即在陣列的元素是實例的T
  • 不安全:如果這取決於這樣的事實,所述陣列是依賴於運行時類型的數組包括T[]

觀光實例:返回它作爲類型,通過它作爲參數傳遞給T[]類型的參數,獲取使用.getClass()陣列型,它傳遞給依賴於運行時類型的陣列,如List.toArray()Arrays.copyOf()的方法等

2)的區別我上面提到的太複雜了,不易自動區分。

+4

現在這一切都有道理。謝謝。所以只是爲了看到我完全理解你,讓我們說我們有一個類'FOO '將可變參數方法的T []作爲一個'Somthing'數組返回是否安全? – Oren

+4

@Oren:應該是 – newacct

+15

可能值得注意的是,使用'@ SafeVarargs'總是正確的一種情況是,您對數組做的唯一處理是將它傳遞給另一個已註釋過的方法(例如:我經常發現自己編寫可變參數方法,使用'Arrays.asList(...)'將它們的參數轉換爲列表並將其傳遞給另一個方法;這種情況總是可以用'@ SafeVarargs'因爲'Arrays.asList'具有註釋)。 – Jules