提防Java中的函数式编程!
這對函數(shù)式編程并不會造成太大的影響,這真棒。 這是關(guān)于某些實(shí)踐的警告,您很可能會將其應(yīng)用于您的代碼,而這完全是錯(cuò)誤的! 。
高階函數(shù)對于函數(shù)式編程是必不可少的,因此,談?wù)撍鼈儗椭蔀榫蹠械慕裹c(diǎn)。
如果您正在編寫JavaScript,那么您就一直在這樣做。 例如:
setTimeout(function() {alert('10 Seconds passed'); }, 10000);上面的setTimeout()函數(shù)是一個(gè)高階函數(shù)。 它是一個(gè)使用匿名函數(shù)作為參數(shù)的函數(shù)。 10秒鐘后,它將調(diào)用作為參數(shù)傳遞的函數(shù)。
我們可以編寫另一個(gè)簡單的高階函數(shù)來提供上述功能:
var message = function(text) {return function() {alert(text);} };setTimeout(message('10 Seconds passed'), 10000);如果執(zhí)行上述操作,將執(zhí)行message() ,并返回一個(gè)匿名函數(shù),該函數(shù)將提醒您傳遞給message()的參數(shù)文本
在函數(shù)式編程中,以上是常見的做法。 從高階函數(shù)返回的函數(shù)將捕獲外部作用域,并在調(diào)用時(shí)能夠?qū)υ撟饔糜蚱鹱饔谩?
為什么在Java中這種做法很危險(xiǎn)?
出于同樣的原因。 從高階“函數(shù)”(方法)返回的“函數(shù)”(lambda)將捕獲外部作用域,并在調(diào)用時(shí)能夠?qū)υ撟饔糜蚱鹱饔谩?
這里給出了最簡單的示例:
class Test {public static void main(String[] args) {Runnable runnable = runnable();runnable.run(); // Breakpoint here}static Runnable runnable() {return () -> {System.out.println("Hello");};} }在上述邏輯中,如果在執(zhí)行runnable.run()調(diào)用的地方放置一個(gè)斷點(diǎn),則可以在堆棧上看到無害的lambda實(shí)例。 一個(gè)簡單的生成類,支持功能接口的實(shí)現(xiàn):
現(xiàn)在,讓我們將此示例轉(zhuǎn)換為普通的Enterprise?應(yīng)用程序(請注意注釋 ),我們已對其進(jìn)行了簡化,以適合此博客文章:
class Test {public static void main(String[] args) {Runnable runnable = new EnterpriseBean().runnable();runnable.run(); // Breakpoint here} }@ImportantDeclaration @NoMoreXML({@CoolNewValidationStuff("Annotations"),@CoolNewValidationStuff("Rock") }) class EnterpriseBean {Object[] enterpriseStateObject = new Object[100_000_000];Runnable runnable() {return () -> {System.out.println("Hello");};} }斷點(diǎn)仍在同一位置。 我們在堆棧上看到了什么?
仍然是一個(gè)無害的小lambda實(shí)例:
精細(xì)。 當(dāng)然。 讓我們添加一些其他日志記錄,僅用于調(diào)試
class Test {public static void main(String[] args) {Runnable runnable = new EnterpriseBean().runnable();runnable.run(); // Breakpoint here} }@ImportantDeclaration @NoMoreXML({@CoolNewValidationStuff("Annotations"),@CoolNewValidationStuff("Rock") }) class EnterpriseBean {Object[] enterpriseStateObject = new Object[100_000_000];Runnable runnable() {return () -> {// Some harmless debugging hereSystem.out.println("Hello from: " + this);};} }哎呀!
突然, this引用的“無害”之處迫使Java編譯器將EnterpriseBean?的封閉實(shí)例包含在返回的Runnable類中:
隨之而來的是沉重的enterpriseStateObject ,現(xiàn)在不再可以對其進(jìn)行垃圾回收,直到調(diào)用站點(diǎn)釋放無害的Runnable小對象為止。
好吧,這現(xiàn)在不是什么新鮮事了嗎?
確實(shí)不是。 Java 8沒有一流的功能,沒關(guān)系。 通過名義上的SAM類型支持lambda表達(dá)式的想法非常狡猾,因?yàn)樗试S升級和lambda-y-fy Java生態(tài)系統(tǒng)中的所有現(xiàn)有庫,而無需對其進(jìn)行更改。
而且,在匿名課堂上,整個(gè)故事也就不足為奇了。 自從舊的Swing 1.0樣式好的ActionListener等以來,以下編碼樣式已通過匿名類泄漏了內(nèi)部狀態(tài)。
class Test {public static void main(String[] args) {Runnable runnable = new EnterpriseBean().runnable();runnable.run();} }@ImportantDeclaration @NoMoreXML({@CoolNewValidationStuff("Annotations"),@CoolNewValidationStuff("Rock") }) class EnterpriseBean {Object[] enterpriseStateObject = new Object[100_000_000];Runnable runnable() {return new Runnable() {@Overridepublic void run() {System.out.println("Hello from " + this);}};} }什么是新的? lambda樣式將鼓勵(lì)在各處使用Java中的高階函數(shù)。 這通常是好的。 但是只有當(dāng)高階函數(shù)是靜態(tài)方法時(shí),其結(jié)果類型才不會包含任何狀態(tài)。
但是,通過以上示例,我們可以看到,在不久的將來,當(dāng)我們開始接受Java 8的功能樣式編程時(shí),將通過一些內(nèi)存泄漏和問題進(jìn)行調(diào)試。
因此,請小心,并遵循以下規(guī)則:
(“純”)高階函數(shù)必須是Java中的靜態(tài)方法!
進(jìn)一步閱讀
封閉實(shí)例之前已引起問題。 了解過去二十年來可怕的雙花括號反模式如何在Java開發(fā)人員中造成痛苦。
翻譯自: https://www.javacodegeeks.com/2015/11/beware-of-functional-programming-in-java.html
總結(jié)
以上是生活随笔為你收集整理的提防Java中的函数式编程!的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: DDOS攻击类型(ddos攻击vps)
- 下一篇: Spark简介,您的下一个REST Ja