2014-12-23 168 views
22

在Java中,以下代碼在兩個查詢上都返回false。爲什麼?方法引用是單身嗎?它肯定會使附加和分離聽衆變得更簡單。因爲您需要爲任何需要進行等效性檢查的方法引用保持常量,所以不能只在每個必需位置使用方法引用運算符。爲什麼不是方法引用singleton?

public class Main { 

    public Main() { 
     // TODO Auto-generated constructor stub 
    } 

    public void doStuff() { 

    } 

    public static void main(String[] args) { 
     Main main = new Main(); 
     Runnable thing1 = main::doStuff; 
     Runnable thing2 = main::doStuff; 
     System.out.println(thing1 == thing2); // false 
     System.out.println(thing1.equals(thing2)); // false 
    } 

} 

回答

16

對於例如方法,我不認爲它將使意義對它們進行緩存。你必須爲每個實例緩存一個方法......這或者意味着與該方法相關的類中有一個額外的字段 - 每個公共方法一個,大概是因爲這些方法可以從類外部引用 - 或者緩存在用戶的方法引用,在這種情況下,你需要某種每個實例的緩存。

我認爲這是更有意義的方法引用靜態方法進行緩存,因爲它們將永遠是相同的。但是,要緩存實際的Runnable,則需要爲其定位的每種類型的緩存。例如:

public interface NotRunnable { 
    void foo(); 
} 

Runnable thing1 = Main::doStuff; // After making doStuff static 
NotRunnable thing2 = Main::doStuff; 

應該thing1thing2相等嗎?編譯器如何知道要創建的類型?它可以在這裏創建兩個實例並分別緩存它們 - 並且始終緩存在使用點而不是方法聲明點。 (你的榜樣具有相同的類聲明的方法和引用它,這是一個非常特殊的情況下,你應該考慮其中,他們是不同的更一般的情況>)

的JLS 允許對於要緩存方法引用。從section 15.13.3

接着,無論是與下面的性質的類的新實例被分配和初始化,或者與下面的屬性的類的現有實例被引用。

...但即使對於靜態方法,似乎javac此刻也不做任何緩存。

+0

那麼爲什麼不重寫.equals()引用的方法?所有需要的將是每個持有對'main'和'Main.class.getMethod(「doStuff」)'的引用(感謝提示@CostiCiudatu) – Dimitriye98

+0

@ Dimitriye98:這絕對是一種可能性 - 但是你會需要精確定義語義。例如,對兩個截然不同但等於對象的實例方法的引用是否相等?也許......但是任何時候你依賴於這樣的equals都會依賴函數的實現細節 - 如果某人手動實現了'Runnable'會怎麼樣呢? –

+0

看起來我的編輯是忍者,因爲我不小心按下了回車鍵而不是shift-enter並提前提交了評論:P – Dimitriye98

4

在這種簡單情況下,您可以根據需要創建額外的靜態或實例字段,如果lambda引用對象則更復雜,但意圖是將這些實例內聯,例如,

List<String> list = .... 
list.stream().filter(s -> !s.isEmpty()).forEach(System.out::println); 

應儘可能高效(甚至創建不超過對象)

for (String s : list) 
    if(!s.isEmpty()) 
     System.out.println(s); 

它可以通過內聯流代碼,並使用逃逸分析以去除需要在第一至創建對象消除的對象地點。

由於這個原因,很少關注實現equals(),hashCode()或toString(),通過閉包的反射來訪問它們。 AFAIK,這故意避免以非預期的方式使用對象。

+0

內聯編譯時?如果是這樣,檢查lambda/method引用是否會被分配給任何東西都不應該成爲問題。 – Dimitriye98

+0

問題不在於它是否適用於任何東西,而是它是否「逃避」該方法,即如果不是,它可以被優化並且被理想地添加到堆棧而不是堆中,前提是您不使用像equals等方法 –

+0

無論哪種方式,編譯器不只是做額外的工作?假設編譯器能夠正確識別何時內聯和何時不內聯,不應該改變性能。 – Dimitriye98

2

單身製作方法將需要同步獲取對他們的引用。這給這樣一個簡單的操作帶來了很大的開銷,並帶來不可預知的結果。另一種解決方案是在類加載中爲每個方法創建對象,但這會導致很多冗餘對象,因爲只有很少的方法需要引用。我認爲同步是主要問題。

相關問題