我最近遇到了java @SafeVarargs註釋。谷歌搜索是什麼使Java的不安全一個可變參數函數留給我搞糊塗了(?堆中毒抹去類型?),所以我想知道的幾件事情:Java SafeVarargs註釋,是否存在標準或最佳實踐?
是什麼讓在一個可變參數的Java功能不安全@SafeVarargs感(最好以深入的例子的形式解釋)?
爲什麼此註釋留給程序員的判斷?這不是編譯器應該能夠檢查的東西嗎?
是否有一些標準必須堅持以確保他的功能確實是安全的?如果沒有,確保它的最佳實踐是什麼?
我最近遇到了java @SafeVarargs註釋。谷歌搜索是什麼使Java的不安全一個可變參數函數留給我搞糊塗了(?堆中毒抹去類型?),所以我想知道的幾件事情:Java SafeVarargs註釋,是否存在標準或最佳實踐?
是什麼讓在一個可變參數的Java功能不安全@SafeVarargs感(最好以深入的例子的形式解釋)?
爲什麼此註釋留給程序員的判斷?這不是編譯器應該能夠檢查的東西嗎?
是否有一些標準必須堅持以確保他的功能確實是安全的?如果沒有,確保它的最佳實踐是什麼?
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[] { ... }
(這裏Object
是T
的上限;如果上限有些不同,則將代替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)的區別我上面提到的太複雜了,不易自動區分。
現在這一切都有道理。謝謝。所以只是爲了看到我完全理解你,讓我們說我們有一個類'FOO
@Oren:應該是 – newacct
可能值得注意的是,使用'@ SafeVarargs'總是正確的一種情況是,您對數組做的唯一處理是將它傳遞給另一個已註釋過的方法(例如:我經常發現自己編寫可變參數方法,使用'Arrays.asList(...)'將它們的參數轉換爲列表並將其傳遞給另一個方法;這種情況總是可以用'@ SafeVarargs'因爲'Arrays.asList'具有註釋)。 – Jules
您是否看過[JavaDoc](http://docs.oracle.com/javase/7/docs/api/java/lang/SafeVarargs.html)中的示例(和解釋)? – jlordo
對於第三個問題,一種做法是始終有第一個元素和其他元素:'retType myMethod(Arg first,Arg ... others)'。如果你沒有指定'first',那麼允許一個空的數組,並且你可能會有一個相同名字的方法,並且沒有參數的返回類型相同,這意味着JVM很難確定哪個方法應該是調用。 – fge
@jlordo我做了,但我不明白爲什麼它在可變參數環境中給出,因爲它可以很容易地補充可變參數函數之外的這種情況(驗證了這一點,在預期的類型安全警告和運行時錯誤編譯)。 – Oren