Java 函數(shù)式編程要點總結(jié)
一、函數(shù)式概念
函數(shù)式編程是一種結(jié)構(gòu)化編程的范式,主要思想是把運算過程盡量寫成系列嵌套的函數(shù)調(diào)用。函數(shù)編程的概念表述帶有很抽象的感覺,可以基于案例看:
public class Function01 {
public static void main(String[] args) {
// 運算:(x+y)* c
int x1 = 2 ;
int y1 = 3 ;
int c1 = 4 ;
int sum1 = x1 + y1 ;
int res1 = sum1 * c1 ;
System.out.println("res1 = "+res1);
}
}
這里基于過程的方式做計算,上面的代碼塊著重在描述程序執(zhí)行過程。
在看基于函數(shù)的方式解決方法:
public class Function02 {
public static void main(String[] args) {
// 函數(shù)式計算
System.out.println("func01 = "+func01(2,3,4));
}
private static int func01 (int x,int y,int c){
return (x+y)*c;
}
}
函數(shù)式編程的核心要素:傳入?yún)?shù),執(zhí)行邏輯,返回值,也可以沒有返回值。
函數(shù)式的編程風格側(cè)重描述程序的執(zhí)行邏輯,不是執(zhí)行過程。
同上面計算過程相比,函數(shù)式編程也減少很多臨時變量的創(chuàng)建,代碼風格也變的簡潔清楚。
二、函數(shù)與方法
在Java語言中有函數(shù)式編程風格,但是Java代碼中沒有函數(shù)的說法,而是稱為:方法;
public class Function03 {
public static void main(String[] args) {
Func03 func03 = new Func03();
func03.add(2);
System.out.println(func03.res1);
}
}
class Func03 {
public int res1 = 0 ;
public void add (int a1){
this.res1 = a1 +1 ;
}
}
類定義引用數(shù)據(jù)類型,類實例化后的對象可以調(diào)用類內(nèi)部的方法和數(shù)據(jù),這是最直觀的感覺。
但是方法又有靜態(tài)和非靜態(tài)的區(qū)別,靜態(tài)方法屬于類所有,類實例化前即可使用。
非靜態(tài)方法可以訪問類中的任何成員變量和方法,并且必須是類實例化后的對象才可以調(diào)用。
三、JDK函數(shù)基礎
1、Lambda表達式
Lambda表達式也可稱為閉包,是推動Java8發(fā)布的最重要新特性,允許把函數(shù)作為一個方法的參數(shù)(函數(shù)作為參數(shù)傳遞進方法中)。
這里就很鮮明的對比Lambda表達式語法和傳統(tǒng)用法。
public class Lambda01 {
interface LambdaOpera {
int operation(int a, int b);
}
public static void main(String[] args) {
LambdaOpera lambdaOpera = new LambdaOpera(){
@Override
public int operation(int a, int b) {
return a * b ;
}
};
System.out.println(lambdaOpera.operation(3,2));
LambdaOpera lambdaOpera01 = (int a, int b) -> a + b;
LambdaOpera lambdaOpera02 = (int a, int b) -> a - b;
System.out.println(lambdaOpera01.operation(3,2));
System.out.println(lambdaOpera02.operation(3,2));
}
}
在看一個直觀的應用案例,基于Lambda的方式創(chuàng)建線程,可以使代碼變的更加簡潔緊湊:
public class Lambda02 {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 2; i++) {
System.out.println(i);
}
}
}).start();
// 對比 Lambda 方式
new Thread(() -> {
for (int i = 0; i < 2; i++) {
System.out.println(i);
}
}).start();
}
}
在看一下Runnable接口的結(jié)構(gòu):
FunctionalInterface標記在接口上,表示該接口是函數(shù)式接口,并且該接口只包含一個抽象方法,
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
Lambda表達式本身可以理解為就是一個接口的實現(xiàn)過程,這里runnable就是完整的Lambda表達式聲明:
public class Lambda04 {
public static void main(String[] args) {
Runnable runnable = () -> {
System.out.println("run one...");
};
Thread thread = new Thread(runnable);
thread.start();
}
}
Lambda表達式最直觀的作用就是使得代碼變得異常簡潔,并且可以作為參數(shù)傳遞。
2、函數(shù)式接口
Lambda表達式雖然有很多優(yōu)點,但是使用的時候需要定義一些接口用來完成編碼,這樣又使得表達式又變得重量級,Java8自身已經(jīng)提供幾個常見的函數(shù)式接口。
- Function:輸入一個參數(shù),返回一個結(jié)果;
- Consumer:輸入一個參數(shù),不返回結(jié)果;
- BiFunction:輸入兩個參數(shù),返回一個結(jié)果;
- BiConsumer:輸入兩個參數(shù),不返回任何結(jié)果;
public class Lambda05 {
public static void main(String[] args) {
Function<Integer, Integer> function01 = x -> x * 2;
System.out.println(function01.apply(2));
BiFunction<Integer, Integer, Integer> function02 = (x, y) -> x * y;
System.out.println(function02.apply(2, 3));
Consumer<String> consumer01 = msg -> System.out.println("msg:"+msg);
consumer01.accept("hello");
BiConsumer<String,Integer> consumer02 = (msg,i)
-> System.out.println(msg+":"+i);
consumer02.accept("world",3);
}
}
如果面對更復雜的業(yè)務需求,可以自定義函數(shù)式接口去解決。
四、Optional類
1、Null判斷
Optional類是Java函數(shù)式編程的應用,主要用來解決常見的空指針異常問題。
在Java編程的開發(fā)中,很多地方都能常見空指針異常的拋出,如果想避免這個問題就要加入很多判斷:
public class Optional01 {
public static void main(String[] args) {
User user = new User(1,"hello") ;
if (user != null){
if (user.getName() != null){
System.out.println(user.getName());
}
}
}
}
為了確保程序不拋出空指針這種低級的錯誤,在程序中隨處可以null的判斷,代碼顯然冗余和繁雜。
2、Optional應用
基于Optional類創(chuàng)建的對象可能包含空值和null值,也同樣會拋出對應的異常:
public class Optional02 {
public static void main(String[] args) {
// NoSuchElementException
Optional<User> optionalUser = Optional.empty();
optionalUser.get();
// NullPointerException
Optional<User> nullOpt = Optional.of(null);
nullOpt.get();
}
}
所以在不明確對象的具體情況下,使用ofNullable()方法:
public class Optional03 {
public static void main(String[] args) {
User user = new User(1,"say");
Optional<User> optionalUser = Optional.ofNullable(user);
if (optionalUser.isPresent()){
System.out.println(optionalUser.get().getName());
}
User user1 = null ;
User createUser = Optional.ofNullable(user1).orElse(createUser());
System.out.println(createUser.getName());
User user2 = null ;
Optional.ofNullable(user2).orElseThrow( ()
-> new RuntimeException());;
}
public static User createUser (){
return new User(2,"hello") ;
}
}
這樣看下來Optional結(jié)合鏈式方法和Lambda表達式就很大程度上簡化了應用的代碼量:
public class Optional04 {
public static void main(String[] args) {
// 1、map轉(zhuǎn)換方法
User user = new User(99, "Java");
// user = null ;
String name = Optional.ofNullable(user)
.map(u -> u.getName()).orElse("c++");
System.out.println(name);
// 2、過濾方法
Optional<User> optUser01 = Optional.ofNullable(user)
.filter(u -> u.getName() != null && u.getName().contains("c++"));
// NoSuchElementException
System.out.println(optUser01.get().getName());
}
}
Optional提供null處理的各種方法,可以簡潔很多代碼判斷,但是在使用風格上和之前變化很大。
五、Stream流
如果Optional簡化很多Null的判斷,那Stream流的API則簡化了很多集合的遍歷判斷,同樣也是基于函數(shù)式編程。

上述為Stream接口繼承關(guān)系如圖,同樣提供一些特定接口和較大的包裝接口,通過源碼查看,可以看到和函數(shù)編程也是密切相關(guān)。
public class Stream01 {
public static void main(String[] args) {
Stream<String> stream = Stream.of("hello", "java");
stream.forEach(str -> System.out.print(str+";"));
}
}
Stream與函數(shù)接口結(jié)合使用,函數(shù)接口又可以使用Lambda表達式進行簡化代碼。在Java8通過Stream可以大量簡化集合使用的代碼復雜度。
public class Stream02 {
public static void main(String[] args) {
// 1、轉(zhuǎn)換Stream
List<String> list = Arrays.asList("java+;", "c++;", "net;");
list.stream();
// 2、forEach操作
list.stream().forEach(System.out::print);
// 3、map映射,輸出 3,4
IntStream.rangeClosed(2,3).map(x->x+1).forEach(System.out::println);
// 4、filter過濾
list.stream().filter(str -> str.contains("+")).forEach(System.out::print);
// 5、distinct去重
Integer[] arr = new Integer[]{3, 1, 3, 1, 2,4};
Stream.of(arr).distinct().forEach(System.out::println);
// 6、sorted排序
Stream.of(arr).sorted().forEach(System.out::println);
// 7、collect轉(zhuǎn)換
List<String> newList = list.stream().filter(str -> str.contains("+"))
.collect(Collectors.toList());
newList.stream().forEach(System.out::print);
}
}
在沒有Stream相關(guān)API之前,對于集合的操作和遍歷都會產(chǎn)生大量的代碼,通過Stream相關(guān)API集合的函數(shù)式編程和Lambda表達式的風格,簡化集合很多操作。
六、源代碼地址
GitHub·地址
https://github.com/cicadasmile/java-base-parent
GitEE·地址
https://gitee.com/cicadasmile/java-base-parent
以上就是Java 函數(shù)式編程要點總結(jié)的詳細內(nèi)容,更多關(guān)于Java 函數(shù)式編程的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
使用Java實現(xiàn)將ppt轉(zhuǎn)換為文本
這篇文章主要為大家詳細介紹了如何使用Java實現(xiàn)將ppt轉(zhuǎn)換為文本,文中的示例代碼簡潔易懂,具有一定的借鑒價值,有需要的小伙伴可以參考下2024-01-01
SpringBoot 如何添加容器啟動的初始化邏輯的操作方法
這篇文章主要介紹了SpringBoot 如何添加容器啟動的初始化邏輯,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-09-09
Hystrix?Turbine聚合監(jiān)控的實現(xiàn)詳解
微服務架構(gòu)下,?個微服務往往部署多個實例,如果每次只能查看單個實例的監(jiān)控,就需要經(jīng)常切換很不?便,在這樣的場景下,我們可以使??Hystrix?Turbine?進?聚合監(jiān)控,它可以把相關(guān)微服務的監(jiān)控數(shù)據(jù)聚合在?起,便于查看2022-09-09
解決springcloud中Feign導入依賴為unknow的情況
這篇文章主要介紹了解決springcloud中Feign導入依賴為unknow的情況,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03

