java基礎-函數式編程入門

一概述

1.1 函數式編程簡介

Java作爲面向對象的編程語言,如果按照編程種類劃分屬於命令式編程(Imperative Programming)。常見的編程範式還有邏輯式編程(Logic Programming),函數式編程(Functional Programming)。

函數式編程作爲一種編程範式,在科學領域,是一種編寫計算機程序數據結構和元素的方式,它把計算過程當做是數學函數的求值,而避免更改狀態和可變數據。

什麼是函數式編程?簡單的回答:一切都是數學函數。函數式編程語言裏也可以有對象,但通常這些對象都是恆定不變的 —— 要麼是函數參數,要什麼是函數返回值。函數式編程語言裏沒有 for/next 循環,因爲這些邏輯意味着有狀態的改變。相替代的是,這種循環邏輯在函數式編程語言裏是通過遞歸、把函數當成參數傳遞的方式實現的。

2. Lambda 表達式

Java 8的最大變化是引入了LambdaLambda 是希臘字母 λ 的英文名稱)表達式——一種緊湊的、傳遞行爲的方式。

2.1 Lambda 表達式的形式

lambda表達式的語法由參數列表、箭頭符號->和函數體組成。函數體既可以是一個表達式,也可以是一個語句塊。
表達式:表達式會被執行然後返回執行結果。
語句塊:語句塊中的語句會被依次執行,就像方法中的語句一樣。
return語句會把控制權交給匿名方法的調用者。
break和continue只能在循環中使用。
如果函數體有返回值,那麼函數體內部的每一條路徑都必須返回值。
表達式函數體適合小型lambda表達式,它消除了return關鍵字,使得語法更加簡潔。
下面是一些lambda表達式:

(int x, int y) ‐> x + y
() ‐> 42
(String s) ‐> { System.out.println(s); }

第一個lambda表達式接收 x y 這兩個整形參數並返回它們的和;第二個lambda表達式不接收參數,返回整數'42';第三個lambda表達式接收一個字符串並把它打印到控制檯,不返回值。

2.2 常見應用

2.2.1 替代匿名內部類

毫無疑問,lambda表達式用得最多的場合就是替代匿名內部類,而實現Runnable接口是匿名內部類的經典例子。lambda表達式的功能相當強大,用()->就可以代替整個匿名內部類!請看代碼:

如果使用匿名內部類:

@Test
public void oldRunable() {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("The old runable now is using!");
}
}).start();
}

 

而如果使用lambda表達式:

@Test
public void runable() {
new Thread(() ‐> System.out.println("It's a lambda function!")).start();
}

 

最後的輸出:

The old runable now is using!
It's a lambda function!

是不是強大到可怕?是不是簡單到可怕?是不是清晰明瞭重點突出到可怕?這就是lambda表達式的可怕之處,用極少的代碼完成了之前一個類做的事情!


2.2.2 使用lambda表達式對集合進行迭代
Java的集合類是日常開發中經常用到的,甚至說沒有哪個java代碼中沒有使用到集合類。。。而對集合類最常見的操作就是進行迭代遍歷了。請看對比:

@Test
public void iterTest() {
List<String> languages = Arrays.asList("java","scala","python"); //before java8
for(String each:languages) {
System.out.println(each);
}
//after java8
languages.forEach(x ‐> System.out.println(x)); languages.forEach(System.out::println);
}

如果熟悉scala的同學,肯定對forEach不陌生。它可以迭代集合中所有的對象,並且將lambda表達式帶入其中。

languages.forEach(System.out::println);

這一行看起來有點像c++裏面作用域解析的寫法,在這裏也是可以的。

 

2.2.3 用lambda表達式實現map
一提到函數式編程,一提到lambda表達式,怎麼能不提map。沒錯,java8肯定也是支持的。請看示例代碼:

@Test
public void mapTest() {
List<Double> cost = Arrays.asList(10.0, 20.0,30.0); cost.stream().map(x ‐> x + x*0.05).forEach(x ‐>
System.out.println(x));
}

 

最後的輸出結果:

10.5
21.0
31.5

map函數可以說是函數式編程裏最重要的一個方法了。map的作用是將一個對象變換爲另外一個。在我們的例子中,就是通過map方法將cost增加了0.05倍的大小然後輸出。


2.2.4 用lambda表達式實現map與reduce
既然提到了map,又怎能不提到reduce。reduce與map一樣,也是函數式編程裏最重要的幾個方法之一。map的作用是將一個對象變爲另外一個,而reduce實現的則是將所有值合併爲一個,請看:

@Test
public void mapReduceTest() {
List<Double> cost = Arrays.asList(10.0, 20.0,30.0);
double allCost = cost.stream().map(x ‐> x+x*0.05).reduce((sum,x) ‐> sum + x).get();
System.out.println(allCost);
}
 

最終的結果爲:

63.0

如果我們用for循環來做這件事情:

@Test
public void sumTest() {
List<Double> cost = Arrays.asList(10.0, 20.0,30.0); double sum = 0;
for(double each:cost) {
each += each * 0.05;
sum += each;
}
System.out.println(sum);
}

相信用map+reduce+lambda表達式的寫法高出不止一個level。


2.2.5 filter操作
filter也是我們經常使用的一個操作。在操作集合的時候,經常需要從原始的集合中過濾掉一部分元素。

@Test
public void filterTest() {
List<Double> cost = Arrays.asList(10.0, 20.0,30.0,40.0); List<Double> filteredCost = cost.stream().filter(x ‐> x >
25.0).collect(Collectors.toList());
filteredCost.forEach(x ‐> System.out.println(x));
}

最後的結果:

30.0

40.0

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章