2017-04-07 307 views
5

下面是我的代碼片段的兩行:Comparator.comparing(...)拋出非靜態引用異常而採取的字符串::的compareTo

List<String> listDevs = Arrays.asList("alvin", "Alchemist", "brutus", "larsen", "jason", "Kevin"); 

listDevs.sort(Comparator.comparing(String::length)); //This works fine 
listDevs.sort(String::compareToIgnoreCase); //This works fine 

但(出expermient的),當我嘗試寫

listDevs.sort(Comparator.comparing(String::compareToIgnoreCase)); 

編譯器會引發錯誤

不能使靜態參考非靜態方法 compareToIgn oreCase(字符串)從字符串類型

類似恰好下面的代碼

listDevs.sort(Comparator.comparing(String::compareTo)); 

我理解的錯誤,並且如果刪除Comparator.comparing它工作正常(如上所示)。

但我要說的是,這如何行工作?

listDevs.sort(Comparator.comparing(String :: length));

我相信我失去了一些東西。我已閱讀this線程。這是相同的情況?

+1

我會說'String :: compareToIgnoreCase'只是不匹配'Comparator.comparing'預期的方法簽名。它期望'String - > Comparable',但是得到'(String,String) - > Integer'。但是不知道錯誤。也許Java試圖將不匹配的方法解釋爲靜態方法,以查看是否匹配...順便說一句,在Eclipse中,我得到了一個不同的,更明智的錯誤消息。 –

回答

10

Comparator.comparing期望一個Function其描述的元件的可比屬性。所以String::length就足夠了,因爲length()String的一個屬性,它將String評估爲int(這就是爲什麼comparingInt在這裏更好)。

相反,String.compareToIgnoreCaseString.compareTo比較方法。他們比較兩個String對象。因此,預計需要Comparator,但對預期屬性爲Function的地方沒有足夠的參考。

這就像你有一個工廠說:「給我一個引擎,我們造一輛車給你」,你試圖給他們一個完整的汽車。雖然現有的汽車在預計汽車有效的情況下有效,但將其傳遞給工廠建造汽車毫無意義。

不幸的是,目前的編譯器實現是和功能特徵報告錯誤非常糟糕。簽名不匹配時,您幾乎總是會看到類似「無法對非靜態方法進行靜態引用...」的消息。

+0

感謝您的輸入。我理解你的觀點,但仍然不服氣。 Comparator.comparing適用於keyExtractors,其中整數是輸入,String是輸出。如果一個方法返回一個整數,不應該有任何問題。 compareTo和length方法的返回類型完全相同。因此,如果我們說話,從句法上來說,比較方法接受這兩種方法應該沒有任何問題。而且,我們同意錯誤信息是誤導性的。 – Vikrant

+2

不,字符串是輸入,整數是輸出,反之亦然。這是'length()'的情況,其中輸入是您調用此方法的字符串,但不適用於'compareTo(String)'等,您可以調用字符串並將* second *字符串作爲參數。當使用對實例方法的未綁定方法引用時,您必須學習將接收器實例計數爲函數參數。否則,無參數方法'length()'也不起作用。 – Holger

+0

我的不好。否則我的意思是。我的意思是密鑰提取器使用字符串生成一個(整數)鍵,基於排序發生。但是這與length()的情況是一樣的,其中integer是排序的因素,這兩個情況都是相似的。 – Vikrant

4

JLS說方法的編譯時聲明參考ReferenceType :: [TypeArguments]標識符可以用不同的方式解釋。

給定一個目標函數類型與n個參數,一組的潛在的適用的方法被識別:

引用類型:: [TypeArguments]標識符具有兩個不同的arities,n和n-1,被認爲是,以說明這種形式引用靜態方法或實例方法的可能性。

表單ReferenceType :: [TypeArguments]標識符的方法引用表達式可以用不同的方式解釋。如果標識符引用實例方法,則隱式lambda表達式具有類型爲這個的額外參數,而如果標識符是指靜態方法。 ReferenceType可能有兩種適用的方法,所以上述搜索算法分別標識它們,因爲每種情況都有不同的參數類型。

Comparator.comparing方法接受Function<T,R extends Comparable<? super R>>。當你使用String::compareToIgnoreCase會報錯,因爲它有兩個參數一個是隱含的這個另一個是比較字符串的方法參數,所以它更像是BiFunction<String,String,Integer>而不是Function<String,Integer>

BiFunction<String, String, Integer> comparator = String::compareToIgnoreCase; 
// you can't assign a BiFunction to a Function 
// because one is incompatiable with another. 
Function<String,Integer> function = comparator; 

Stream.sort方法接受Comparator,和比較更像是BiFunction<T,T,Integer>因此它是與compatiable String::compareToIgnoreCase。另一方面,它們可以互換。例如:

Comparator<String> primary = String::compareToIgnoreCase; 
BiFunction<String, String, Integer> comparator1 = primary::compare; 
Comparator<String> comparator2 = comparator1::apply; 

您可以使用comparing(String::toLowerCase)相反,它是equalivent到String::compareToIgnoreCase,例如:

// String::compareToIgnoreCase 
listDevs.sort(String::compareToIgnoreCase); 

// comparing(String::toLowerCase) 
listDevs.sort(comparing(String::toLowerCase)) 
5

sort方法預期Comparator

當你這樣做,你確實提供了一個。

同樣的情況,在這裏(但有點不直觀):

listDevs.sort(String::compareToIgnoreCase) 
listDevs.sort((left, right) -> left.compareToIgnoreCase(right)); // same thing as above 

這正是一個Comparator的定義 - 兩個字符串,並返回一個int。

你說這是怎麼來的線路:listDevs.sort(Comparator.comparing(String::length));其實很簡單。

Comparator.comparing需要Function將您的輸入類型轉換爲Comparable。在你的情況下需要String並返回Integer;這是可比較的。

相關問題