您的位置:  首頁 > 技術 > java語言 > 正文

00 后 Dromara 成員帶你學 Java Lambda 函數編程

2022-08-25 11:00 https://my.oschina.net/dromara/blog/5569471 Dromara開源組織 次閱讀 條評論

我的名字叫阿超 年齡?21?歲 家在灶王鎮西南部的別墅區區內 未婚 我在一家普通公司做?Java?開發 每天最晚也會在八點前回家 不抽煙 偶爾沾點酒 晚上十二點上床 保證睡足八個小時 睡前寫一篇博客 再做二十分鐘仰臥起坐暖身 然后再睡覺 基本能熟睡到天亮 像嬰兒一樣不留下任何疲勞和壓力 就這樣迎來第二天的早晨 醫生都說我很正常。

阿超是新晉?Dromara?開源組織成員、Hutool Committer

目前技術棧包括不限于?Java、JavaScript、TypeScript、React、Vue、Python?等

熱愛做開源,給?Sa-Token、Easy-ES?等項目都貢獻過代碼

正文

距離我加入?hutool-commiter?已經有一段時間了,想起曾經封裝過的一個類?Opt,就是使用?lambda?解決空指針問題。按照慣例,先介紹下?dromara?組織下的項目?hutool

Hutool 是一個小而全的 Java 工具類庫,通過靜態方法封裝,降低相關 API 的學習成本,提高工作效率,使 Java 擁有函數式語言般的優雅,讓 Java 語言也可以 “甜甜的”。

這個類?Opt?的靈感來源是對?jdk?內置的?java.util.Optional?的拓展,在一些細節方面進行了簡化處理????

// 之前:String username;if (user != null && user.getUsername() != null) { ?username = user.getUsername();} else { ?username = "no name";}// 現在:String username = Opt.ofNullable(user).map(User::getUsername).orElse("no name");

關于?Opt?和?Optional?的差異我們先按下不表

下面主要是通過其介紹?lambda?的使用

快速上手

依靠?idea?編譯器的提示進行快速上手

下方是判斷?user?是否為空,不為空通過?User#getSchool()?獲取學校名的操作

例如此處我寫到這里

User user = new User();// idea提示下方參數,果沒顯示,光標放到括號里按ctrl+p主動呼出 ? ? ? ? ? ? ? ? ? ? |Function<? super User,?> mapper|Opt.ofNullable(user).map()

這里?idea?為我們提示了參數類型,可這個?Function?我也不知道它是個什么

實際上,我們?new?它一個就好了

Opt.ofNullable(user).map(new Fun) ? ? ? ? ? ? ? ? ? ? ? ? ? ?|Function<User, Object>{...} (java.util.function) ? | ?<-戳我 ? ? ? ? ? ? ? ? ? ? ? ? ? ?|Func<P,R> cn.hutool.core.lang.func ? ? ? ? ? ? ? ? |

這里?idea?提示了剩下的代碼,我們選?Function?就行了,接下來如下:

Opt.ofNullable(user).map(new Function<User, Object>() {})

此處開始編譯報錯了,不要著急,我們這里根據具體操作選取返回值

例如這里是想判斷?user?是否為空,不為空時調用?getSchool,從而獲取其中的返回值?String?類型的?school

我們就如下寫法,將第二個泛型,也就是象征返回值的泛型改為?String

Opt.ofNullable(user).map(new Function<User, String>() {})

然后這里紅線提示了,我們就使用?idea?的修復所有,默認快捷鍵?alt+ 回車???????

Opt.ofNullable(user).map(new Function<User, String>() {}) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| 💡 Implement methods ? ? ? ? ? ? ? ? ?| ?<-選我 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| ? ?Introduce local variable ? ? ? ? ?| ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| ? ?Rollback changes in current line ? |

選擇第一個?Implement methods?即可,這時候彈出一個框,提示讓你選擇你想要實現的方法

這里就選擇我們的?apply?方法吧,按下一個回車就可以了,或者點擊選中?apply,再按一下?OK?按鈕???????

||IJ| Select Methods to Implement ? ? ? ? ? ? ? ? ? ? ? ?X || ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?|| 👇 ?? ?| ?? ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?|| -------------------------------------------------------- || | java.util.function.Function ? ? ? ? ? ? ? ? ? ? ? ? ? ?|| | ? 🔓 apply(t:T):R ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | ? ? ?<-選我選我| | ? 🔓 compose(before:Function<? super V,? extents T):Fu|| | ? 🔓 andThen(after:Function<? super R,? extends V>):Fu|| | ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?|| | ======================================== ? ? ? ? ? ? ? | ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| -------------------------------------------------------- || ?? Copy JavaDoc ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?|| ?? Insert @Override ? ? ? ? ? ? ? | ?OK ?| ?| CANCEL | ? | ? ? <-選完點我點我

此時此刻,代碼變成了這樣子???????

Opt.ofNullable(user).map(new Function<User, String>() { ? ?@Override ? ?public String apply(User user) { ? ? ? ?return null; ? ?}})

這里重寫的方法里面就寫你自己的邏輯 (別忘了補全后面的分號)????

Opt.ofNullable(user).map(new Function<User, String>() { ? ?@Override ? ?public String apply(User user) { ? ? ? ?return user.getSchool(); ? ?}});

我們可以看到,上邊的?new Function()?變成了灰色

我們在它上面按一下?alt+enter(回車)???????

Opt.ofNullable(user).map(new Function<User, String>() { ? ?@Override ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| 💡 Replace with lambda ? ? ? ? ? ? > | ?<-選我啦 ? ?public String apply(User user) { ? ? ? | 💡 Replace with method reference ? > | ? ? ? ?return user.getSchool(); ? ? ? ? ? | 💎 balabala... ? ? ? ? ? ? ? ? ? ? > | ? ?}});

選擇第一個?Replace with lambda,就會自動縮寫為?lambda?啦

Opt.ofNullable(user).map(user1 -> user1.getSchool());

如果選擇第二個,則會縮寫為我們雙冒號格式

Opt.ofNullable(user).map(User::getSchool);

接下來我們獲取值即可???????

// 這里的school可能為nullString school = Opt.ofNullable(user).map(User::getSchool).get();// 如果想要為其提供默認值,可以使用orElseString school = Opt.ofNullable(user).map(User::getSchool).orElse("NO_SCHOOL");// 如果想要為null時才調用方法為其提供默認值,可以使用orElseGet// 這在一些為null時 使用db新增并返回值的場景下很有用String school = Opt.ofNullable(user).map(User::getSchool).orElseGet(() -> User.getDefaultSchool());

進階

簡單來說:函數式編程就是把我們的函數 (方法) 作為參數 / 變量傳遞、調用等,有點像反射的?Method?對象,都是作為函數的載體

例子:自定義函數式接口???????

import java.io.Serializable;
/** * 可序列化的Functional * * @author VampireAchao * @since 2021/6/13 16:42 */@FunctionalInterfacepublic interface Func<T, R> extends Serializable {
 ? ?/** ? ? * 調用 ? ? * ? ? * @param t 參數 ? ? * @return 返回值 ? ? */ ? ?R apply(T t);}

我們定義一個類可以去實現該接口??????????????

/** * 可序列化的函數式接口實現類 * * @author VampireAchao * @since 2021/6/13 16:45 */public class FuncImpl implements Func<Object, String> { ? ?/** ? ? * 調用 ? ? * ? ? * @param o 參數 ? ? * @return 返回值 ? ? */ ? ?@Override ? ?public String apply(Object o) { ? ? ? ?return o.toString(); ? ?}}

這里就有個問題:假設我有很多的地方需要不同的類去實現?Func,我就得每次都去寫這么一個類,然后實現該接口并重寫方法

這樣很麻煩!因此我們使用匿名內部類???????

 ? ? ? ?Func<String, Integer> func = new Func<String, Integer>() { ? ? ? ? ? ?/** ? ? ? ? ? ? * 調用 ? ? ? ? ? ? * ? ? ? ? ? ? * @param s 參數 ? ? ? ? ? ? * @return 返回值 ? ? ? ? ? ? */ ? ? ? ? ? ?@Override ? ? ? ? ? ?public Integer apply(String s) { ? ? ? ? ? ? ? ?return s.hashCode(); ? ? ? ? ? ?} ? ? ? ?};

我們可以看到,使用了匿名內部類后不用每次去新建這個類了,只需要在調用的地方,new?一下接口,創建一個匿名內部類即可

但這樣還有個問題,我們每次都要寫這么一大堆,特別麻煩

由此而生,我們有了?lambda?這種簡寫的形式???????

Func<String, String> func1 = (String s) -> { ? ?return s.toUpperCase();};

如果只有一行,我們可以省略掉中括號以及?return

Func<String, String> func2 = (String s) -> s.toUpperCase();

我們可以省略掉后邊?lambda?中的參數類型,因為前面已經定義過了,編譯器能自動推斷

 ? ? ? ?Func<String, String> func3 = s -> s.toUpperCase();

如果我們滿足特定的形式,我們還可以使用雙冒號的形式縮寫

Func<String, String> func4 = String::toUpperCase;

這里除了我們的參數 -> 返回值寫法:

s-> s.toUpperCase()

還有例如無參數帶返回值寫法?()->"yes"、無參無返回值寫法?()->{}?等等

而雙冒號這種寫法至少有如下幾種:??????

package com.ruben;
import java.util.function.Function;import java.util.function.IntFunction;import java.util.function.Supplier;
/** * 語法糖——雙冒號寫法:: * * @author VampireAchao * @since 2021/7/1 17:44 */public class MethodReferences {
 ? ?public static Object staticSupplier() { ? ? ? ?return "whatever"; ? ?}
 ? ?public Object instanceSupplier() { ? ? ? ?return "whatever"; ? ?}
 ? ?public Object anonymousInstanceFunction() { ? ? ? ?return "whatever"; ? ?}
 ? ?public static void main(String[] args) { ? ? ? ?// 引用構造函數 ? ? ? ?Supplier<MethodReferences> conSup = () -> new MethodReferences(); ? ? ? ?conSup = MethodReferences::new; ? ? ? ?// 數組構造函數引用 ? ? ? ?IntFunction<int[]> intFunction = value -> new int[value]; ? ? ? ?// intFunc == new int[20]; ? ? ? ?int[] intFuncResult = intFunction.apply(20); ? ? ? ?// 引用靜態方法 ? ? ? ?Supplier<Object> statSup = () -> staticSupplier(); ? ? ? ?statSup = MethodReferences::staticSupplier; ? ? ? ?Object statSupResult = statSup.get(); ? ? ? ?// 引用特定對象的實例方法 ? ? ? ?Supplier<Object> instSup = new MethodReferences()::instanceSupplier; ? ? ? ?instSup = new MethodReferences()::instanceSupplier; ? ? ? ?Object instSupResult = instSup.get(); ? ? ? ?// 引用特定類型的任意對象的實例方法 ? ? ? ?Function<MethodReferences, Object> anonInstFunc = streamDemo -> streamDemo.anonymousInstanceFunction(); ? ? ? ?anonInstFunc = MethodReferences::anonymousInstanceFunction;
 ? ?}
}

順便放幾個常用的函數式接口寫法???????

package com.ruben;
import java.math.BigDecimal;import java.util.function.*;
/** * 常用的幾個函數式接口寫法 * * @author VampireAchao * @since 2021/7/1 17:44 */class Usual {
 ? ?public static Consumer<Object> consumer() { ? ? ? ?// 有參數無返回值 ? ? ? ?return o -> { ? ? ? ?}; ? ?}
 ? ?public static Function<Integer, Object> function() { ? ? ? ?// 有參數有返回值 ? ? ? ?return o -> o; ? ?}
 ? ?public static Predicate<Object> predicate() { ? ? ? ?// 有參數,返回值為boolean,注意 o -> null 寫法調用test執行lambda時會NPE ? ? ? ?return o -> true; ? ?}
 ? ?public static Supplier<Object> supplier() { ? ? ? ?// 無參數有返回值 ? ? ? ?return Object::new; ? ?}
 ? ?public static BiConsumer<String, Integer> biConsumer() { ? ? ? ?// 倆參數無返回值 ? ? ? ?return (q, o) -> { ? ? ? ?}; ? ?}
 ? ?public static BiFunction<Integer, Long, BigDecimal> biFunction() { ? ? ? ?// 倆參數,有返回值 ? ? ? ?return (q, o) -> new BigDecimal(q).add(BigDecimal.valueOf(o)); ? ?}
 ? ?public static UnaryOperator<Object> unaryOperator() { ? ? ? ?// 一個參數,返回值類型和參數一樣 ? ? ? ?return q -> q; ? ?}
 ? ?public static BinaryOperator<Object> binaryOperator() { ? ? ? ?// 倆參數和返回值類型保持一致 ? ? ? ?return (a, o) -> a; ? ?}
}

?

Stream 介紹

Java 8 API 添加了一個新的抽象稱為流 Stream,可以讓你以一種聲明的方式處理數據。方法全是傳入函數作為參數,來達到我們的目的。???????

// 聲明式編程是告訴計算機需要計算“什么”而不是“如何”去計算// 現在,我想要一個List,包含3個數字8List<Integer> list = ? ? ? ?// 我想要: ? ? ? ?Stream ? ? ? ?// 8 ? ? ? ?.generate(() -> 8) ? ? ? ?// 3個 ? ? ? ?.limit(3) ? ? ? ?// 最后收集起來轉為List ? ? ? ?.collect(Collectors.toList());// 結果 [8, 8, 8]

Stream?使用一種類似用?SQL?語句從數據庫查詢數據的直觀方式來提供一種對?Java?集合運算和表達的高階抽象。???????

// 就像sql里的排序、截取// 我要把傳入的list逆序,然后從第五個(元素下標為4)開始取值,取4條List<String> list = Arrays.asList("dromara", "Hmily", "Hutool", "Sa-Token", "Jpom", "TLog", "Cubic", "Koalas-rpc", "Fast Request");list = list.stream() ? ? ? ?// 排序(按照自然順序的逆序) ? ? ? ?.sorted(Comparator.reverseOrder()) ? ? ? ?// 從下標為4開始取值 ? ? ? ?.skip(4) ? ? ? ?// 取4條 ? ? ? ?.limit(4) ? ? ? ?// 最后收集起來轉為List ? ? ? ?.collect(Collectors.toList());// 結果 [Jpom, Hutool, Hmily, Fast Request]

Stream API?可以極大提高?Java?程序員的生產力,讓程序員寫出高效率、干凈、簡潔的代碼???????

/** * 老辦法實現一個list,存儲3個8,并轉換為String * * @return [8, 8, 8] */private static List<String> oldFunc() { ? ?List<Integer> list = Arrays.asList(8, 8, 8); ? ?List<String> stringList = new ArrayList<>(list.size()); ? ?for (Integer integer : list) { ? ? ? ?stringList.add(String.valueOf(integer)); ? ?} ? ?return stringList;}
/** * Stream實現一個list,存儲3個8,并轉換為String * * @return [8, 8, 8] */private static List<String> newFunc() { ? ?return Stream.generate(() -> 8).limit(3).map(String::valueOf).collect(Collectors.toList());}

生成?26?個大寫字母組成的集合???????

List<Character> abc = Stream.iterate('A', i -> (char) (i + 1)).limit(26).collect(Collectors.toList());// [A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z]

這種風格將要處理的元素集合看作一種流, 流在管道中傳輸, 并且可以在管道的節點上進行處理, 比如篩選, 排序,聚合等。???????

// 管道中傳輸,節點中處理int pipe = abc.stream() ? ? ? ?// 篩選大于G的字母 ? ? ? ?.filter(i -> i > 'G') ? ? ? ?// 排序 按照自然排序順序逆序 ? ? ? ?.sorted(Comparator.reverseOrder()) ? ? ? ?.mapToInt(Object::hashCode) ? ? ? ?// 聚合求和 ? ? ? ?.sum();// 1539

元素流在管道中經過中間操作(intermediate operation)的處理,最后由最終操作?(terminal operation)?得到前面處理的結果。???????

// 將26個大寫字母Character集合轉換為String然后轉換為小寫字符List<String> terminalOperation = abc.stream() ? ? ? ?// 中間操作(intermediate operation) ? ? ? ?.map(String::valueOf).map(String::toLowerCase) ? ? ? ?// 最終操作 ? ? ? ?.collect(Collectors.toList());// [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z]

?

EasyStream

這是即將推出的?hutool-6.x?新特新之一,對?Stream?進行了進一步簡化 (設計靈感來源新版?jdk?新特性以及日常使用痛點),例如:???????

// 使用toList代替collect(Collectors.toList())List<String> toList = EasyStream.of(list).map(String::valueOf).toList();// 使用toMap代替collect(Collectors.toMap(String::valueOf, Function.identity()))Map<String, Integer> identityMap = EasyStream.of(list).toMap(String::valueOf);// 可以通過三參數創建無限流,提供終止條件List<Integer> list = EasyStream.iterate(0, i -> i < 3, i -> i + 1).toList();// 提供遍歷下標List<String> list = Arrays.asList("dromara", "hutool", "sweet");List<String> mapIndex = EasyStream.of(list).mapIdx((e, i) -> i + 1 + "." + e).toList();// 結果為 ["1.dromara", "2.hutool", "3.sweet"]// ...

當然這個類還在完善中,目前只是一個實驗性功能,還存在一些爭議和問題,后續優化完成后會開放使用

EasyStream?源碼二維碼,大家別忘了點個?star

也歡迎大家來到知識星球和我互動

???????

展開閱讀全文
  • 0
    感動
  • 0
    路過
  • 0
    高興
  • 0
    難過
  • 0
    搞笑
  • 0
    無聊
  • 0
    憤怒
  • 0
    同情
熱度排行
友情鏈接
18禁高潮出水呻吟娇喘mp3,日本熟妇乱人伦A片免费高清,成人午夜精品无码区,狠狠色噜噜色狠狠狠综合久久,麻豆一区二区99久久久久,年轻的妈妈4,少妇被又大又粗又爽毛片,护士张开腿让我爽了一夜,男男互攻互受h啪肉np文,你好神枪手电视剧免费观看啊,97人妻一区二区精品免费,久久久婷婷五月亚洲97号色,freegaysexvideos男男中国,国产精品国产三级国av麻豆,国产精品又黄又爽又色无遮挡网站,亚洲av无码一区二区三区网站,亚洲国产精品久久久久蜜桃,国产真人无码作爱视频免费,国产成人精品亚洲一区二区三区,亚洲欧洲日产最新,老司机带带我精彩免费,国产成人久久精品激情,日本最新av免费一区二区三区,边摸边吃奶又黄又激烈视频
<蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <文本链> <文本链> <文本链> <文本链> <文本链> <文本链>