2017-07-25 64 views
3

剛剛發現了一個奇怪而有趣的Lambda行爲。Lambda:使用Runnable實例啓動的可調用變量

讓我們有下面的類:

private class Task implements Runnable { 
    @Override 
    public void run() { 
     // something to process 
    } 
} 

下面的語句是編譯和運行:

Callable task = Task::new; 

有人能解釋爲什麼這是可能的嗎?

編輯

基於下面的回答,請檢查下面的語句:

1.

ExecutorService executor = Executors.newSingleThreadExecutor(); executor.submit(Task::new);

2.

ExecutorService executor = Executors.newSingleThreadExecutor(); executor.submit(new Task());

乍一看,看起來是一樣的,但實際上做了一個完全不同的事情。

這裏發生的事情正是上述情況。

的原因是ExecutorService有兩種方法:

submit(Runnable); submit(Callable);

因此,使用代碼從1執行人將處理以下關於它的內螺紋:

new Task()

2.版本實際上將調用submit(Runnable)方法,並且Task.run中的代碼將被執行。

結論:只要小心使用Lambda表達式:)

+2

它被稱爲[方法參考](https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html) –

+2

當不使用*原始類型*:'Callable 任務時變得更清晰= Task :: new;'或'Callable task = Task :: new;' – Holger

+0

Holger,我認爲在這種情況下並不重要。最終結果是一樣的。 –

回答

4

可調用是Runnable實例初始化時,它與方法參照Task初始化構造函數在執行時產生 a Runnable

換句話說,如果您執行Callable,它會返回具有尚未被運行新Task對象。那Task執行Runnable在這裏其實是完全不相關的。

如果您不使用原始類型,這將更加清晰。 Task::new可以分配給Callable<Task>,因爲它是不帶參數並返回任務的。

2

要實現Callable<V>接口必須實現與簽名V call()的方法。

因此,您可以使用任何方法的方法引用來實現此接口,並且返回一些引用類型,其中包括構造方法引用,如Task::new

事實上,具有參數的構造函數任何類可用於這種方式:

Callable<SomeClass> callable = SomeClass::new; 
+0

我在這個問題中增加了一個真實場景,請查看。我認爲在聲明Callable時添加泛型不會改變任何內容。 –

+0

@GyuriMajercsik既然你已經接受了答案,我假設你有你正在尋找的答案。 – Eran

+0

是的,答案是Lambda的「方法參考」功能。 –