2017-08-06 85 views
4

考慮下面的代碼示例:執法在方法普通類型等式約束參數

@SafeVarargs 
public static <U> Object[] sortedCopy(Comparator<? super U> comparator, U... values) { 
    U[] copy = Arrays.copyOf(values, values.length); 
    Arrays.sort(copy, comparator); 
    return copy; //copy is implicitly cast to Object[] -> no heap pollution 
} 

public static <U> Object[] sortedCopy(U... values) { 
    return sortedCopy(Comparator.naturalOrder(), values); //why does this compile??? 
} 

我本來期望編譯器拒絕sortedCopy(U...)線,具有以下的理由是:

的返回類型Comparator.naturalOrder()Comparator<T>,其中T是此方法的泛型類型參數,必須滿足約束T extends Comparable<? super T>。由於該方法不接受任何參數,並且其在上述代碼中的調用沒有明確指定類型爲T(如Comparator.<U>naturalOrder(),由於U未擴展Comparable,因此Comparator.<U>naturalOrder()無法編譯),因此必須以其他方式推斷出T。我可以看到推斷T的唯一方法是通過方法簽名sortedCopy(Comparator<? super U>, U...)。編譯器知道values的類型,因此可以推斷出U,並且繼而可以推斷出邊界爲T,即有界通配符? super U。但是編譯器應該認識到,任何? super U都不能滿足由Comparator.naturalOrder()指定的T的要求,即T extends Comparable<? super T>,因爲U本身不延伸Comparable<? super U>,所以U的任何超類都不能。

讓我感到困惑的是編輯器確實在將簽名從sortedCopy(U...)更改爲sortedCopy(U[])時生成錯誤。我想這與第二種情況有關,U作爲數組的類型在運行時存在,而在第一種情況下,它不是。但我還是不明白爲什麼這將使問題的有效方法調用,這是因爲:

  1. 據我瞭解,一般類型的可變參數參數轉換爲Object[]如果值傳遞給方法因爲可變參數是通用的,因此是不可確定的類型,如果我理解正確,那麼在上面的代碼中就是這種情況,因爲sortedCopy(U...)U是不可確定的。但即使如此,爲什麼編譯器沒有意識到Object沒有擴展Comparable<? super Object>
  2. 前面的參數討論運行時類型。但是,我們仍然是預編譯的,所以關於運行時類型的猜測甚至不應該在這種情況下相關,因爲儘管U在運行時可能不再存在,但編譯器仍然知道它,並且應該能夠檢查不管方法參數是一個數組還是一個可變參數,都會實現等式約束。

那麼,爲什麼上述代碼示例中的問題仍在編譯?

除此之外,如果方法sortedCopy(Comparator<? super U>, U...)@SafeVarargs註釋不合適,我也將非常感謝您的反饋。我相信是這樣,但我對此沒有信心。

回答

3

可變參數可能有點鬼鬼祟祟。我在我的IDE中看到的是,它是關於sortedCopy(U... values)作爲遞歸方法,這意味着它不選擇Comparator作爲其第一個參數的重載參數。

如果將此參數從可變參數更改爲數組參數並傳入int[],則會出現您所期望的編譯失敗。

Error:(12, 16) no suitable method found for sortedCopy(java.util.Comparator<T>,U[]) 
    method Foo.<U>sortedCopy(java.util.Comparator<? super U>,U[]) is not applicable 
     (inferred type does not conform to upper bound(s) 
     inferred: U 
     upper bound(s): java.lang.Comparable<? super U>,U,java.lang.Object) 
    method Foo.<U>sortedCopy(U[]) is not applicable 
     (cannot infer type-variable(s) U 
     (actual and formal argument lists differ in length)) 

Error:(18, 47) no suitable method found for sortedCopy(java.util.Comparator<T>,int[]) 
    method Foo.<U>sortedCopy(java.util.Comparator<? super U>,U[]) is not applicable 
     (inference variable U has incompatible bounds 
     equality constraints: int 
     upper bounds: java.lang.Object) 
    method Foo.<U>sortedCopy(U[]) is not applicable 
     (cannot infer type-variable(s) U 
     (actual and formal argument lists differ in length)) 

如果在Integer[]通過,或者如果你只在可變參數的形式傳遞,你只得到了第一個錯誤。

關於picking the most specific method的規則可用於詞法分析器。具體而言,你要由該規則燃燒:

A型S是比任何表達類型T更具體若S <:T.

更詳細地:

T類型的子類型都是U類型,所以T是U的超類型和空類型。

由此,我們推斷,因爲一般不能很好地結合,它接受Comparable爲您的可變參數的一部分。這就是爲什麼我的IDE將其作爲遞歸方法來撿起它,爲什麼當我運行它時,我得到一個StackOverflowError

這個問題實際上消失,如果你正確地綁定的泛型<U extends Comparable<U>>,以確保它不會拿起任何東西,你不能切實排序上...

public static <U extends Comparable<U>> Object[] sortedCopy(Comparator<? super U> comparator, U... values) { 
    U[] copy = Arrays.copyOf(values, values.length); 
    Arrays.sort(copy, comparator); 
    return copy; 
} 

public static <U extends Comparable<U>> Object[] sortedCopy(U... values) { 
    return sortedCopy(Comparator.naturalOrder(), values); 
} 

...並警告你可能現在有堆污染,而且它會更簡單,更簡潔,而不是引入固定方法。

+0

如果你改變了第二個方法的名字,很明顯第一個不是被'sortedCopy(Comparator.naturalOrder(),values)調用的' – Javier

+0

@Javier:我不確定你要去哪裏。如果您更改方法的名稱,那麼不存在任何含糊之處。我的直覺就是保持意圖,並用泛型來糾正問題。 – Makoto

+0

這只是另一種證明「不選擇比較器作爲第一個參數的重載參數」的方法。 OP提示'(比較器比較器,U ...值)'適用於實際參數'(Comparator.naturalOrder(),values)',但事實上並非如此。 – Javier

1

在我看來,這個功能是在對你很重要的場景中調用自己的。

在第二種方法中,您可以用Object替換U,並且所有內容都適合,因爲它是可變參數。