函數式的思考
這裏有個假設問題:目前有3個線程,分別是線程A、B、C,然後這3個線程分別去訪問functionXXX,這就意味着functionXXX的內部變化是會影響到線程A、B、C的,除去函數自身本職工作之外,其還影響其他的業務,這種函數是帶有副作用的。
所謂的副作用就是函數的效果已經超出了函數自身的範疇,而與之相反的函數則稱爲:純粹的函數或者無副作用的函數。
爲了方便引入概念,我這裏再舉個例子:甲方要求對數據庫中的蘋果進行篩選,篩選出重量大於150g,且顏色是淡黃色的蘋果?
實現甲方這種需求,從目前來看,就2種方式:
方式一:專注於如何實現,通過大量的if判斷、for循環遍歷、return等進行編寫,這種方式又叫做:“命令式”編程。
方式二:專注於要做什麼,通過lambda表達式,直接用()->getWieght>150&& getColor = “yellow”就可以完成。這種方式把遍歷細節留給了底層API,這種方式又叫做:“聲明式”編程。
“聲明式”編程比“命令式”編程的優點在於:“聲明式”編程一眼就能明白甲方的需求,就像是在描述甲方的需求,而“命令式”編程則需要你去看函數,去理清函數的前後邏輯以及函數內部邏輯。
“聲明式”編程背後有2層含義:
①爲了讓開發者更加關注於業務,而不是底層實現細節,由系統來選擇如何實現。
②既然API底層已經足夠強大,那麼開發者應該使用不相互影響的表達式來描述自己想要做什麼。
再結合上面提到的無副作用函數,最終在這一指導思想下發展出了:函數式編程。
那麼函數式編程中的“函數”怎麼去理解?
答:這裏的“函數”,特指的就是無副作用函數,即不會產生/修改可變的共享變量的函數。因此,函數式編程通俗來講就是:一種使用無副作用函數進行編程的方式。
JAVA語言的函數式編程,其理想化的編程準則如下:
①被稱爲“函數式”的函數或方法都只能修改本地變量。如果引起其他對象,則它所引用的對象都應該是不可修改的對象。通過這種規定,我們期望所有的字段都爲final類型,所有的引用類型字段都指向不可變對象。
②被稱爲函數式的函數或者方法,不應該拋出任何異常。
函數式編程還有個特點就是引用透明性。
制都隱含着引用透明性。如果一個函數只要傳遞同樣的參數值,總是返回同樣的結果,那這個函數就是引用透明的。
換句話說,函數無論在何處調用、何時調用,如果使用同樣的輸入總能持續地得到相同的結果,就具備了函數式的特徵。
通俗來講就是,無論是單線程訪問,還是多線程併發訪問,使用同樣的輸入總能持續地得到相同的結果,也就說即便是多線訪問,某個線程的修改對其他線程無影響,包括返回結果、共享變量的可見性等。
一等函數:能夠像普通變量一樣使用的函數稱爲一等函數。這是Java 8補充的全新內容:通過::操作符,你可以創建一個方法引用,像使用函數值一樣使用方法,也能使用Lambda表達式(比如, (int x) -> x + 1)直接表示方法的值。 Java 8中使用下面這樣的方法引用將一個方法引用保存到一個變量是合理合法的:
Function<String, Integer> strToInt = Integer::parseInt;
函數式編程的世界裏,如果函數能滿足下面任一要求就可以被稱爲高階函數:接受至少一個函數作爲參數、返回的結果是一個函數。