Java方法引用是如何計算值的
Java如今已經是全球程式設計語言排名第一的語言,運用廣泛,前景廣闊,Java方法引用是如何計算值的?下面就一起來了解看看吧!
除了 lambda 表示式,Java SE 8 引入了方法引用作為簡寫符號。這些主要用於引用靜態方法(例如 Double :: toString)或建構函式(egString [] :: new),這些用法是直接的。 然而,對例項方法的方法引用會以令人驚訝的方式產生與lambda表示式不同的結果。 這是因為方法引用的呼叫目標(在 :: 之前的部分)在首次遇到它的宣告時被求值,而 lambda 表示式僅在實際呼叫時被求值。
1呼叫例項方法
以下程式以各種方式呼叫例項方法,使用方法引用和 lambda 表示式來演示這種不同的行為。下面我們將通過輸出案例來看看發生了什麼。
class MethodRefTest {
public static void main(String[] args) {
tln("Constructor in method reference");
final Runnable newRef = new Counter()::show;
tln("Running...");
(); ();
tln("Constructor in lambda expression");
final Runnable newLambda = () -> new Counter()();
tln("Running...");
(); ();
tln("Factory in method reference");
final Runnable createRef = te()::show;
tln("Running...");
(); ();
tln("Factory in lambda expression");
final Runnable createLambda = () -> te()();
tln("Running...");
(); ();
tln("Variable in method reference");
obj = new Counter(); // NPE if after method reference declaration!
final Runnable varRef = obj::show;
tln("Running...");
(); obj = new Counter(); ();
tln("Variable in lambda expression");
obj = null; // no NPE, lambda expression declaration not evaluated
final Runnable varLambda = () -> ();
tln("Running...");
obj = new Counter(); ();
obj = new Counter(); ();
tln("Getter in method reference");
final Runnable getRef = get()::show;
tln("Running...");
(); obj = new Counter(); ();
tln("Getter in lambda expression");
final Runnable getLambda = () -> get()();
tln("Running...");
(); obj = new Counter(); ();
}
static Counter obj;
static Counter get() {
t("get: ");
return obj;
}
static class Counter {
static int count;
final int myCount;
Counter() {
myCount = count++;
tln(at("new Counter(%d)", myCount));
}
static Counter create() {
t("create: ");
return new Counter();
}
void show() {
tln(at("Counter(%d)()", myCount));
}
}
}
2構造方法
第一個塊的程式碼在直接建立的 Counter 類的新例項上呼叫方法。 此類跟蹤在當前執行期間建立的例項數,並且通過其建立索引標識每個例項。 下面是輸出結果:
Constructor in method reference
new Counter(0)
Running...
Counter(0)()
Counter(0)()
Constructor in lambda expression
Running...
new Counter(1)
Counter(1)()
new Counter(2)
Counter(2)()
方法引用和 lambda 表示式呼叫一樣都被呼叫了兩次,正確的輸出了兩次 show 方法。 但是,在方法引用中,指定的構造方法只在宣告時呼叫了一次。 然後重用建立好的物件。該 lambda 表示式在宣告時不執行任何操作,而是在每次執行時呼叫建構函式。
3工廠方法
第二段程式碼實際上等同於第一段,但使用工廠方法而不是建構函式來獲取newCounter物件。 結果與以前相同,表明方法表示式的不同順序與在呼叫目標表達式中直接新建物件的順序無關。
通過方法引用來呼叫工廠方法
create: new Counter(3)
Running...
Counter(3)()
Counter(3)()
通過lambda 表示式呼叫工廠方法
Running...
create: new Counter(4)
Counter(4)()
create: new Counter(5)
Counter(5)()
4變數訪問
第三段程式碼測試了(不同方式下的)變數訪問,這裡由於lambda表示式不接受可變的本地變數而使用了靜態欄位.
使用方法引用的變數訪問
new Counter(6)
Running...
Counter(6)()
new Counter(7)
Counter(6)()
使用lambda表示式的變數訪問
Running...
new Counter(8)
Counter(8)()
new Counter(9)
Counter(9)()
方法引用對它的呼叫目標立即求值會造成兩個結果.
一是,欄位初始化必須在(方法引用)宣告前,否則會發生da表示式卻不是這種情況:我們可以在(lambda表示式)宣告前將欄位重置為null---只要我們在呼叫時該欄位有有效值即可.
二是,由於目標物件的引用儲存的是欄位的即時值,所以當該欄位接下來被賦值為一個新建的Counter,目標物件的引用也不會變.於此不同地是,lambda表示式每次執行時都會去取欄位當前的值.
5取值器(Getter)方法
最後一段程式碼與變數訪問的`例子等效,只是使用了getter方法來讀取欄位當前的值.又一次,在後面這個例子中,這個輔助字元再兩次呼叫之間發生了改變.
使用方法引用的Getter
get: Running...
Counter(9)()
new Counter(10)
Counter(9)()
使用lambda表示式的Getter
Running...
get: Counter(10)()
new Counter(11)
get: Counter(11)()
在方法引用的例子中,get:在Running...之前只出現了一次,說明getter在宣告的時候只調用了一次.因此,返回的欄位值被用於兩次show方法呼叫da表示式呼叫了兩次getter,在第二次呼叫時獲取到了更新的欄位值.
分析
上述行為在Java SE 8 語言規範的§15.13.3 “方法引用的執行時求值”的最後註釋中被描述:
方法引用表示式求值的時機比lambda表示式更復雜(§15.27.4).當一個方法引用表示式在:: 分隔符前有一個表示式(而不是一個型別)時,這個子表示式將會被立即求值.求值的結果會被一直儲存,直到相關的函式介面型別被呼叫;那個時候,求值的結果將會被用作呼叫的目標引用.這表明在::分隔符之前的表示式只在程式進入方法引用表示式時求值,並且不會在接下來的函式介面型別呼叫中重新求值.
發現
我第一次注意到方法引用與lambda表示式的不同是當我像在建構函式測試例子中那樣嘗試使用方法引用在一個MenuItem 處理器中建立對話方塊例項時.我驚訝地發現每次呼叫會開啟帶著上次調所有控制內容的完全一樣的例項.使用lambda表示式替換方法引用後才產生期望的行為----每次呼叫建立一個新的對話方塊.
方法引用的立即對目標求值很有可能不是使用者想要的行為,在大多數情況下,我們期望目標在呼叫間改變.你可以考慮只對靜態方法和建構函式(X::new)使用方法引用,或者和那些對所有呼叫都確定不會改變的例項引用一起使用.如果目標引用有任何需要動態重新求值的可能,你就必須使用lambda表示式.
-
編寫更好的Java單元測試的7個技巧
測試是開發的一個非常重要的方面,可以在很大程度上決定一個應用程式的命運。良好的測試可以在早期捕獲導致應用程式崩潰的問題,但較差的測試往往總是導致故障和停機。雖然有三種主要型別的軟體測試:單元測試,功能測試和整合測試,但是在這篇博文中,我們將討論開發人員...
-
Java語法基礎for語句練習
本文是本站小編搜尋整理的關於Java語法基礎for語句練習,供參考學習,希望對大家有所幫助!想了解更多相關資訊請持續關注我們應屆畢業生考試網!控制語句——for練習語句的巢狀應用累加求和,計數器迴圈巢狀一、語句的巢狀應用語句巢狀形式。其實就是語句中...
-
Java中的物件與引用知識詳解
在Java中,有一組名詞經常一起出現,它們就是“物件和物件引用”,很多朋友在初學Java的時候可能經常會混淆這2個概念,覺得它們是一回事,事實上則不然。今天我們就來一起了解一下物件和物件引用之間的區別和聯絡。以下僅供參考!1.何謂物件?在Java中有一句比較流行的話,叫...
-
Java 正則表示式
Java是一門程式語言,那麼大家知道Java正則表示式是怎樣的呢?下面一起來看看!Java正則表示式正則表示式定義了字串的模式。正則表示式可以用來搜尋、編輯或處理文字。正則表示式並不僅限於某一種語言,但是在每種語言中有細微的差別。正則表示式例項一個字串其...