為什麼函數式編程在Java中很危險?

2021-01-18 CSDN技術社區

在我的日常工作中,我身邊的開發者大多是畢業於CS編程頂級院校比如MIT、CMU以及Chicago,他們初次涉及的語言是Haskell、Scheme及Lisp。他們認為函數式編程是一種自然的、直觀的、美麗的且高效的編程樣式。但奇怪的是,我和我的同事並沒有為Haskell、Scheme、Lisp、Clojure、Scala而編程,這個行業裡的絕大部分人都會使用Python、 Ruby、Java或C#等編程,因為它們用起來比較順手。但在Java中,函數式編程卻是低效且危險的。

為什麼函數式編程在Java中很危險呢?

每隔幾個月,我都會在調試中發現問題,究其原因最終可追溯到濫用函數的想法以及編程算法,更重要的原因是這個虛擬機無法創建這種編程樣式。

最近Bob Martin想出一個很好的例子並說明了原因。Clojure (一個真正的函數式編程)返回到25整數列表:

(take 25 (squares-of (integers))) 

此代碼運行和響應速度都很快,輸出結果:

(1 4 9 16 25 36 49 64 … 576 625) 

現在,假設我們想要在Java中重寫,如果我們以Gosling的方式來編寫Java,那麼該代碼是簡單、快速且明顯的:

for (int i=1; i<=25; i++)  System.out.println(i*i);  } 

但是,現在假設我們讓它變得多功能性,在特定的假設範圍內重置上面的Clojure樣式:

嘗試運行吧,OK,從堆轉儲(Heap Dump)中恢復 ?

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space  at java.util.Arrays.copyOf(Arrays.java:2760)  at java.util.Arrays.copyOf(Arrays.java:2734)  at java.util.ArrayList.ensureCapacity(ArrayList.java:167)  at java.util.ArrayList.add(ArrayList.java:351)  at Take25.integers(Take25.java:30)  at Take25.main(Take25.java:9) 

當Java輸出後,Clojure如何處理函數,使該函數可返回到每一個int?

Clojure如同所有真正的函數語言(與Java不同)具備懶散賦值特性。它(指clojure)不會計算不被使用的值。它可以遠離這個,因為Clojure不像Java,它是真正函數式語言,可以假定變量不發生變異,使求值的順序變得無關緊要。因此,Clojure可以執行優化,但是Java編譯器卻不能——這就是為什麼函數式編程在Java中是危險的原因。因為,Java不是真正的函數式語言,JIT和javac無法像在一個真正的函數式語言中積極且有效地優化函數構造對象,比如返回無窮個列表的標準函數計算,都是Java程序的死穴。這也是為什麼函數式編程在Java中危險的原因。

這裡,也許你會反對我的觀點,OK,你無須在Java中返回所有的整數列表(或者甚至是所有的ints);但是相信沒人做到這一點。

我們來一起看看比較現實的做法。這裡我再次使用遞歸來計算而不是循環:

public class Squares {   public static void main(String args[]) {  squareAndPrint(1, Integer.parseInt(args[0]));  }   public static void squareAndPrint(int n, int max) {  System.out.println(n * n);  if (max > n) {  squareAndPrint(n + 1, max);  }  }   } 

開始運行!

很抱歉,堆棧溢出。這就是為什麼在XOM中我小心翼翼地使用循環,即使遞歸的地方十分清楚也不使用遞歸。否則,精心配置XML文檔可能會造成XOM-using程序來轉儲核心。因此,避免在非函數式語言中進行大量遞歸,正如Java和C不僅僅是性能需求,也是安全方面的要求。

寫在最後:

我不是說函數式編程不好,也不是說函數式編程低效,其實,我熱愛函數式編程。就像我的同事認為函數式編程是自然、直觀且美麗的編程風格,但當它作為一們語言比如為Haskell重新設計時,Java中函數語句的性能Bug絕對能要了你的命。

英文出自:Cafe.elharo

相關焦點

  • Java中的函數式接口
    什麼是函數式接口java中函數式接口中的定義如下:函數式接口(Functional Interface)就是一個有且僅有一個抽象方法,但是可以有多個非抽象方法的接口。Java中的函數式接口JDK中自帶了一些函數式接口,主要可以分為兩類:在JDK1.8之前已經存在的函數式接口(包括單不限於這些):java.lang.Runnablejava.util.concurrent.Callablejava.security.PrivilegedActionjava.util.Comparatorjava.io.FileFilterjava.nio.file.PathMatcherjava.lang.reflect.InvocationHandlerjava.beans.PropertyChangeListenerjava.awt.event.ActionListenerjavax
  • 這樣理解 Java 中的函數式編程就對了
    在眾多的編程範式中,大多數開發人員比較熟悉的是面向對象編程範式。一方面是由於面向對象程式語言比較流行,與之相關的資源比較豐富,比如Java,c++等。另外一方面是由於大部分學校和培訓機構的課程設置,都選擇流行的面向對象程式語言。面向對象編程範式的優點在於其抽象方式與現實中的概念比較相近。比如,學生、課程、汽車和訂單等這些現實中的概念,在抽象成相應的類之後,我們很容易就能理解類之間的關聯關係。
  • Java新特性之-函數式編程
    在眾多的編程範式中,大多數開發人員比較熟悉的是面向對象編程範式。一方面是由於面向對象程式語言比較流行,與之相關的資源比較豐富,比如Java,c++等。另外一方面是由於大部分學校和培訓機構的課程設置,都選擇流行的面向對象程式語言。
  • 深入理解Java中的Lambda表達式和函數式編程的關係
    上一篇文章(https://www.toutiao.com/i6824085046475883022/),我們理解了函數式編程基本思想、概念和Java對函數式編程的基本使用。本篇我們來聊聊lambda表達式和函數式編程的關係。當提到 Java 8 的時候,Lambda 表達式總是第一個提到的新特性。
  • Java之Lambda函數式編程應用舉例,鏈式語法「真乾貨來拿走」
    Java之Lambda函數式編程應用舉例,鏈式語法「真乾貨來拿走」 java 8 Lambda函數式編程,像阿里、騰訊這樣的大網際網路公司早就已經使用的技術。學習下jdk8的新特性,對提高開發效率和寫出缺陷更低的代碼都非常有好處,時代在進步程式語言也在進化。
  • 純Java中的函數式編程:Functor和Monad示例
    絕大多數程式設計師,特別是那些沒有功能編程背景的程式設計師,都傾向於認為monad是某種神秘的計算機科學概念,因此從理論上講,它對他們的編程事業沒有幫助。這種消極的觀點可以歸因於數十篇文章或博客文章過於抽象或過於狹窄。但是事實證明,即使在標準Java庫中,monad也無處不在,尤其是從Java Development Kit(JDK)8開始(以後會有更多介紹)。
  • 函數式編程簡介—Lambda 表達式
    Java作為面向對象的程式語言,如果按照編程種類劃分屬於命令式編程(Imperative Programming)。常見的編程範式還有邏輯式編程(Logic Programming),函數式編程(Functional Programming)。
  • Java如何支持函數式編程?
    但是在其它的程式語言中,如JS、C++,我們可以直接寫一個函數,然後在需要的時候進行調用,既可以說是面向對象編程,也可以說是函數式編程。從功能上來看,面向對象編程沒什麼不好的地方,但是從開發的角度來看,面向對象編程會多寫很多可能是重複的代碼行。
  • Java函數式編程快速入門:Lambda表達式與Stream API
    Python、JavaScript等當紅語言對函數式編程支持都不錯,Scala更是以函數式編程的優勢在大數據領域攻城略地,即使是老牌的Java為了適應函數式編程,也加大對函數式編程的支持。未來的程式設計師或多或少都要了解一些函數式編程思想。本文拋開一些數學推理等各類複雜的概念,從使用的角度帶領讀者入門函數式編程。
  • 組合軟體:為什麼要在 JavaScript 中學習函數式編程?
    最後從零開始探索 ES6 和 函數式編程!希望所有新概念在這個過程中都被解釋到了 - 但是別指望會太舒適。如果你是一個已經熟悉 JavaScript 或者純函數式語言的老手,也許你會認為用 JavaScript 來探究函數式編程簡直個笑話。請把這些想法放一邊,試著用開放的心態來接觸這份資料。
  • Java 8必將掀起Java函數式編程熱潮
    很明顯,這個版本是過去十年以來推出的最具份量的Java更新,其中囊括了海量新特性,包括默認方法、方法與構造函數引用以及Lambda函數等等。  其中最有趣的一項特性當數全新java.util.streamAPI,它作為Javadoc狀態存在,能夠對元素流進行函數式操作,例如在集合中進行map-reduce變換。
  • 面試被虐題:說說 JVM 系語言的函數式編程
    其理論基礎也並非為編程而設計,而是一種數學抽象(Lamda演算),其實初中就學過了,λ表達式。在 JS(建議把 JS 作為函數式編程思想學習的入門語言,Java 的實現略顯臃腫,可能不太便於理解)當中,函數式編程算是應用比較多的了。各現代高級程式語言,都或多或少地支持了函數式編程。
  • java8的函數式編程解析
    其實在java8就已經有java的函數式編程寫法,只是難度較大,大家都習慣了對象式用法,但在其它語言中都有函數式的用法,如js,scala,函數式其實是抽象到極致的思想。什麼是函數式編程 函數式編程並不是Java新提出的概念,其與指令編程相比,強調函數的計算比指令的計算更重要;與過程化編程相比,其中函數的計算可以隨時調用。
  • 函數式編程是未來的最佳編碼範例嗎?
    簡而言之,函數式編程需要為固定變量構建純函數並通過其響應更改狀態。 相反,其他程式語言具有通過更改程序中變量引用來更改應用程式狀態的能力。 您可以在本文中了解功能編程與面向對象程序的更多區別。由於其純粹的性質,函數式編程對於令人垂涎的任務(如機器學習和數據分析)印象深刻。
  • 函數式編程
    來源:酷殼網-陳皓 連結:https://coolshell.cn/articles/10822.html當我們說起函數式編程來說,我們會看到如下函數式編程的長相:函數式編程的三大特性函數式編程的幾個技術map & reduce:這個技術不用多說了,函數式編程最常見的技術就是對一個集合做Map和Reduce操作。這比起過程式的語言來說,在代碼上要更容易閱讀。
  • 函數式編程如何能在常規軟體開發中佔一席之地
    除非你生活中與世隔絕的深山老林裡,否則你應該知道,在眾多的所謂頂級編程高手(alpha geeks)中,函數式編程是十分盛行的。也許你已經使用了某種函數式程式語言。如果你是在使用很傳統的程式語言,例如Java或C#,你應該知道了,這些語言很快就將引入一些函數式編程特徵。就在這美麗的新世界即將來到之際,就在我們摩拳擦掌打算大幹一番之前,我想,現在應該是我們暫停一下、反省一下函數式編程在我們的日常應用開發中是否合適的好時機。 什麼是函數式編程?簡單的回答:一切都是數學函數。
  • Java Stream函數式編程?用過都說好,案例圖文詳解送給你
    Java Stream函數式編程接口最初是在Java 8中引入的,並且與lambda一起成為Java開發的裡程碑式的功能特性,它極大的方便了開放人員處理集合類數據的效率。從筆者之前看過的調查文章顯示,絕大部分的開發者使用的JDK版本是java 8,其中Java Stream和lambda功不可沒。
  • 函數式編程/lambda表達式入門
    函數式編程/lambda表達式入門本篇主要講解 lambda表達式的入門,涉及為什麼使用函數式編程,以及jdk8提供的函數式接口 和 接口的默認方法 等等1.什麼是命令式編程命令式編程就是我們去告訴程序如何實現
  • 大數據中java與mapreduce編程
    、map、shuffle、reduce、outputshuffle過程包括sort、copy、combine操作reduce之前有時涉及二次排序mr運行過程示意圖mr開發方式1、hadoop streaming執行mapreduce2、hive執行mapreduce3、java MR編程hadoop
  • 大數據中java與mapreduce編程
    ,比如rownumber等窗口函數可擴展性好,比如自定義存儲格式、自定義函數UDF多接口,比如JDBC、Thrift、Rest等缺點:不能用於複雜計算,比如涉及時序處理的數據。示例java MR編程用java編寫MR在以前和現在都有廣泛應用