Java函數式編程(三)
- 2020 年 4 月 2 日
- 筆記
前言
如果你對Lambda表達式感興趣,想知道它怎麼幫助你提升專業技能,那麼,看到這篇文章就對了!我們假設大家還不知道Lambda表達式,以及Java的核心類庫的變化,我們將從「零」開始介紹這些概念、類庫和技術。
Standing on Shoulders of Giants
站在巨人的肩膀上
OR
如果你曾使用過匿名內部類,也許遇到過這樣的情況:需要引用它所在方法里的變量。這時,需要將變量聲明為final。這裡我們給出一個Demo:
(匿名內部類中使用final局部變量)
final String name = getUserName(); button.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent event){ System.out.println("hi "+ name); } });
將變量聲明為final,意味着不能為其重複賦值。同時也意味着在使用final變量時,實際上是在使用賦給該變量的一個特定的值。
從Java 8開始雖然放鬆了這一限制,可以引用非final變量,但是該變量在既成事實上必須是final。雖然無需將變量聲明為final,但在Lambda表達式中,也無法用作終態變量。如果堅持用作終態變量,編譯器就會報錯。
既成事實上的final是指只能給該變量賦值一次。換句話說,Lambda表達式引用的是值,而不是變量。我們給出一個Demo:
(name就是一個既成事實上的final變量)
String name = getUserName(); button.addActionListener(event -> System.out.println("hi " + name));
final就好像是代碼中的線路噪聲,省去之後代碼更易讀。當然了,在很多情況下,顯式地使用final代碼更易懂。是否使用完全取決於個人喜好。
如果你試圖給該變量多次賦值,然後在Lambda表達式中引用它,編譯器就會報錯。我們給出一個Demo:
(未使用既成事實上的final變量,導致無法通過編譯)
String name = getUserName(); name = formatUserName(name); button.addActionListener(event -> System.out.println("hi " + name));
編譯器會顯示出錯信息:local variables referenced from a Lambda expression must be final or effectively final.
Lambda表達式中引用的局部變量必須是final或既成事實上的final變量。
實際上這種行為也解釋了為什麼Lambda表達式也被稱為閉包。未賦值的變量與周邊環境隔離起來,進而被綁定到一個特定的值。在圈子裡,Java是否擁有真正的閉包一直備受爭議,因為在Java中只能引用既成事實上的final變量。「名字雖異,功能相同」。但是,這裡要說的是,無論名字如何,Lambda表達式都是靜態類型的。因此我們在下一期將會分析Lambda表達式本身的類型:函數接口。
每周小結
• 在很多情況下,顯式地使用final代碼更易懂。是否使用完全取決於個人喜好。
每周名言
• 如今的編程是一場程序員和上帝的競賽,程序員要開發出更大更好、傻瓜都會用到軟件。而上帝在努力創造出更大更傻的傻瓜。目前為止,上帝是贏的。(Rick Cook)
每周問答
• 問 :我知道C++定義了一種名為析構函數(destructor)的特殊函數,可以在銷毀對象時自動執行,finalize()與析構函數一樣嗎?
• 答 :Java沒有析構函數。儘管finalize()方法與析構函數的功能相近,但兩者不一樣。例如,C++析構函數總是在對象不在作用域之前被調用,但是對於任何具體對象,都不知道如何調用finalize()。坦率的講,因為Java使用了垃圾回收,所以沒有必要使用析構函數了。


