目錄
- 一、函數式編程
- 1.1 什么是函數式編程
- 1.2 函數式編程特征
- 1.2.1 純函數
- 1.2.2 函數是一等公民
- 1.3 函數式編程在java中的實踐
- 參考資料
一、函數式編程
1.1 什么是函數式編程
函數式編程(Functional Programming)是一種編程范式,它將計算視為數學函數的求值,強調使用純函數和不可變數據。
除了函數式編程之外, 還有 命令式編程,聲明式編程 等編程范式。
在函數式編程中,函數是一等公民,它可以作為參數傳遞給其他函數,也可以作為返回值返回。函數式編程避免了副作用(Side Effects)和共享狀態,這樣可以提高代碼的可維護性、可測試性和并發性。
1.2 函數式編程特征
以下是函數式編程的一些主要特征:
- 純函數(Pure Functions): 函數的輸出只依賴于輸入,不依賴于任何外部狀態或可變數據。相同的輸入始終產生相同的輸出,而且沒有副作用。
- 不可變數據(Immutable Data): 數據一旦被創建就不能被更改。如果需要修改數據,通常會創建一個新的數據副本。
- 函數是一等公民(First-Class Functions): 函數跟其它的數據類型一樣處于平等地位,可以被當作參數傳遞給其他函數,也可以被作為返回值返回。
- 高階函數(Higher-Order Functions): 函數可以接受一個或多個函數作為參數,并且可以返回一個函數。
- 遞歸(Recursion): 在函數式編程中,遞歸是一種常見的控制結構,因為循環通常依賴于可變狀態,而函數式編程強調不可變性。
- Lambda 表達式: Lambda 表達式是一種匿名函數,它允許在代碼中直接定義簡短的函數。
- 惰性求值(Lazy Evaluation): 只在需要時計算表達式的值,而不是在每個可能的地方都計算。
- 模塊化和組合性(Modularity and Compositionality): 將問題分解為小的、可復用的函數,通過組合這些函數來構建更復雜的功能。
函數式編程最重要的特征是 純函數 和 函數是一等公民
函數式編程不是一個特定語言的概念,而是一種編程風格或哲學。雖然一些編程語言(如Haskell、Scala、Clojure)更天然地支持函數式編程,但在越來越多的編程語言中,如Java、JavaScript、Python,也引入了函數式編程的特性。函數式編程的思想對于處理并發、提高代碼可維護性和表達力都有很大的幫助。
1.2.1 純函數
純函數是指相同的輸入總會得到相同的輸出,并且不會產生副作用的函數。純函數的兩個特點:
- 相同的輸入必有同輸出。
- 無副作用。無副作用 指的是函數內部的操作不會對外部產生影響(如修改全局變量的值等)。
舉個例子,
public class Person{private int a = 1;public int add(int b){return a + b;}public int pow(int c){return c * c;}public static void main(String[] args){Person p = new Person();p.add(1);p.pow(2);}
}
上面代碼中add(int b)這個方法就不符合函數式編程,這個函數調用后的結果不確定,它的結果不僅取決于b還取決于字段a。而pow(int c)這函數就是符合函數式編程的典范,只要調用它,輸入的值c確定了返回值就肯定確定了。
1.2.2 函數是一等公民
在編程語言中,將函數視為一等公民(First-Class Citizen)意味著函數具有以下特性:
- 可以賦值給變量: 可以將函數賦值給變量,使得函數可以像其他數據類型一樣在程序中被存儲和傳遞。
- 可以作為參數傳遞: 可以將函數作為參數傳遞給其他函數,這使得函數可以參與更高階的操作,如函數的組合和轉換。
- 可以作為返回值: 函數可以作為其他函數的返回值,使得函數可以生成和返回其他函數。
- 可以存儲在數據結構中: 函數可以存儲在數組、列表、集合等數據結構中,允許對函數進行組織和管理。
在支持函數作為一等公民的編程語言中,函數與其他數據類型(如整數、字符串等)具有相同的地位,可以像操作其他數據一樣靈活地使用。這種特性是函數式編程風格的核心之一。
舉個簡單的例子,假設有一個接受兩個整數和一個函數作為參數的函數 operate
:
@FunctionalInterface
public interface IntBinaryOperator {int applyAsInt(int left, int right);// 其他默認方法和靜態方法...
}public class FunctionAsFirstClassCitizenExample {// 函數作為參數public static int operate(int a, int b, IntBinaryOperator operator) {return operator.applyAsInt(a, b);}// 函數作為返回值public static IntBinaryOperator getOperator(String operation) {switch (operation) {case "+":return (x, y) -> x + y;case "-":return (x, y) -> x - y;case "*":return (x, y) -> x * y;default:throw new IllegalArgumentException("Unsupported operation: " + operation);}}public static void main(String[] args) {// 函數賦值給變量IntBinaryOperator addition = (x, y) -> x + y;// 函數作為參數傳遞int result = operate(5, 3, addition);System.out.println("Result of addition: " + result);// 函數作為返回值IntBinaryOperator selectedOperator = getOperator("*");// 調用返回的函數int result2 = operate(4, 2, selectedOperator);System.out.println("Result of multiplication: " + result2);}}
在這個例子中,IntBinaryOperator
是 Java 中的一個函數式接口,用于表示接受兩個整數參數并返回一個整數結果的操作。它定義了一個抽象方法 applyAsInt(int left, int right)
,用于執行具體的整數操作。函數 operate
接受兩個整數和一個函數作為參數,函數 getOperator
返回一個函數。。這展示了函數作為一等公民的特性,可以方便地傳遞和組合。
1.3 函數式編程在java中的實踐
Java 8 引入了對函數式編程的支持,主要通過以下幾個特性來實現:
-
Lambda 表達式: Lambda 表達式是 Java 8 中引入的一個重要特性,它使得能夠以更為簡潔的方式表示匿名函數。Lambda 表達式可以用來實現函數式接口(只包含一個抽象方法的接口)。
javaCopy code// 舊的方式使用匿名內部類 Runnable runnable1 = new Runnable() {@Overridepublic void run() {System.out.println("Hello, World!");} };// 使用 Lambda 表達式 Runnable runnable2 = () -> System.out.println("Hello, World!");
-
函數式接口: 函數式接口是一個只包含一個抽象方法的接口。Java 8 引入了
@FunctionalInterface
注解來標識函數式接口,以便編譯器檢查。常見的函數式接口包括Runnable
、Callable
、Comparator
等。javaCopy code@FunctionalInterface interface MyFunction {void apply(); }
-
Stream API: Stream API 提供了一種對集合進行聲明式操作的方式,支持函數式編程風格的數據處理。它允許對集合進行過濾、映射、歸約等操作,而不需要顯式使用循環。
javaCopy codeList<String> words = Arrays.asList("apple", "banana", "orange");// 使用 Stream 進行操作 long count = words.stream().filter(word -> word.length() > 5).count();
-
默認方法和靜態方法: 接口中引入的默認方法和靜態方法使得在接口中引入新方法變得更為靈活。這對于在函數式編程中向接口添加新功能非常有用,而不會破壞現有實現。
javaCopy codeinterface MyInterface {default void defaultMethod() {System.out.println("Default method");}static void staticMethod() {System.out.println("Static method");}void abstractMethod(); }
-
Optional 類:
Optional
類是 Java 8 引入的一個用于處理可能為 null 的值的容器類。它通過一系列的函數式方法來支持更安全、更函數式的處理空值的方式。javaCopy codeOptional<String> name = Optional.ofNullable(getName()); String result = name.orElse("Unknown");
這些特性使得 Java 在某種程度上支持函數式編程范式,盡管 Java 仍然是一門面向對象編程的語言。除了以上特性,一些第三方庫,如Guava和Vavr,提供了更多函數式編程支持的工具和數據類型。在 Java 9、Java 10 和 Java 11 中,也進一步增強了函數式編程的支持,例如引入了模塊化系統和新的集合操作方法。
參考資料
什么是「函數式編程」? - 知乎 (zhihu.com)
詳解Java中的三種函數式編程,以及具體的實現方法 | w3cschool筆記