2013-10-24 22 views
1

閱讀一些openjdk7的代碼,我已經找到方法Collections.reverse爲實現(我已經刪除了RandomAccess的列出了一些優化):爲什麼openjdk使用不安全的構造?

public static void reverse(List<?> list) { 
    int size = list.size(); 
    ListIterator fwd = list.listIterator(); 
    ListIterator rev = list.listIterator(size); 
    for (int i = 0, mid = list.size() >> 1; i < mid; i++) { 
     Object tmp = fwd.next(); 
     fwd.set(rev.previous()); 
     rev.set(tmp); 
    } 
} 

凡ListIterators兩個初始化產生unchecked警告(沒有@SupressWarnings註解在代碼中)。

對我實施的保存和簡單的方法是:

public static <E> void reverse2(List<E> list) { 
    int size = list.size(); 
    ListIterator<E> fwd = list.listIterator(); 
    ListIterator<E> rev = list.listIterator(size); 
    for (int i = 0, mid = list.size() >> 1; i < mid; i++) { 
     E tmp = fwd.next(); 
     fwd.set(rev.previous()); 
     rev.set(tmp); 
    } 
} 

這是完全安全的。

我的問題是:

  • 爲什麼OpenJDK的使用使用原始類型的不安全的代碼?
  • 爲什麼警告不被抑制?
+0

相信點有避免必須命名類型的變量。 –

回答

0

這是一個權衡。 JDK開發人員接受警告,以減少其他開發人員的警告。如果您使用List(即原始類型)編譯舊代碼,則可以將其傳遞到Collections.reverse(),因爲它是安全的,因此不會發出警告。但是,如果Collections.reverse()的類型參數與public static <E> void reverse(List<E> list)類似,則在嘗試使用原始類型List調用它時收到警告。

+0

謝謝。這非常合理。 –

0

概念,reverse()只是需要任何參數的List。它根本不在乎,也不需要參數與其他類型相關。因此,void reverse(List<?> list)是最簡單的(因此也是最好的)簽名。

現在,它正確地寫在仿製藥中,reverse實現內部,有必要用列表的類型參數,因爲我們需要從列表中選擇元素,並把它放回去,我們需要一個類型來表達這個臨時值。

在Java中做到這一點的唯一方法是有宣佈整個方法的泛型參數 - 使其成爲一個通用的方法 - 爲您的reverse2做:<E> void reverse2(List<E> list)

但是,這改變了簽名(其對外部可見)。這裏的E僅在參數中的一個地方使用,從類型的角度來看基本上是不必要的。我們需要在內部使用E的事實是外部代碼不需要關心的實現細節。

有一種方法可以使用完全安全的結構並保留簽名void reverse(List<?> list):使用捕獲幫助程序。即做一個助手方法,它List<?>,並且它是所有調用泛型方法(然後可以爲私有):

public static void reverse(List<?> list) { 
    reverse2(list); 
} 

(如果你不明白這是爲什麼在泛型正確,提示:這是由於捕獲。)

儘管這似乎有兩全其美的(更簡單的簽名+安全的構造),不足之處是,它是從來看,浪費一個運行點。我們必須採取一個參數,它是所有直接傳遞採取一個參數的另一種方法的方法。它看起來完全無關緊要。類型擦除後 - 你將有一個方法採取List呼籲採取List另一種方法。 (如果我向您展示了這樣的功能,並且您不知道泛型,您可能會立即認爲這是不必要的。)

因此,爲了提高效率,他們決定在內部代碼中犧牲完全安全的構造,以保持簡單的簽名和單個函數調用的效率而不是兩個。他們使用的結構是在內部代碼無論如何,所以沒有人,但Java庫,開發者應該關心它;類型擦除後,代碼是完全一樣的安全代碼,所以就在二進制沒有區別。而編譯代碼的簽名和效率都會影響庫的用戶。

相關問題