Kotlin基礎4-擴展函數、函數式編程

一.擴展函數

1.定義擴展函數

擴展函數可以在不直接修改類定義的情況下增加類功能,擴展函數可以用于自定義類,也可以用于比如List、String,以及Kotlin標準庫里的其他類。和繼承相似,擴展函數也能共享類行為,在你無法接觸某個類定義或者某個類沒有使用open修飾符,導致你無法繼承它時,擴展函數就是增加類功能的最好選擇。
定義擴展函數和定義一般函數差不多,但有一點不大一樣,除了函數定義,你還需要指定接收功能擴展的的接收者類型。

image.png

運行結果:
abc!!!!!!!!!!
test
15

在Kotlin文件中定義的擴展函數全局都可以用,如果不想暴露給全局使用,可以使用private修飾

2.泛型擴展函數

如果想在調用addExt擴展函數之前和之后分別打印字符串怎么辦?

image.png

直接將easyPrint擴展函數新增Any返回值,在main方法中調用會報錯,因為addExt是String的擴展函數,Any是String的父類,所以easyPrint方法返回必須是String才能調用,如下:

image.png

執行結果:
abc
abc!!!!!!!!!!

除了上邊的方法外,還有別的方法,那就是泛型擴展函數
新的泛型擴展函數不僅可以支持任何類型的接收者,還保留了接收者的類型信息,使用泛型類型后,擴展函數能夠支持更多類型的接收者,適用范圍更廣了。

image.png

泛型擴展函數在Kotlin標準庫里隨處可見,例如let函數,let函數被定義成了泛型擴展函數,所以能支持任何類型,它接收了一個lambda表達式,這個lambda表達式的接收者T作為值參,返回的R-lambda表達式返回的任何新類型。

image.png

二.擴展屬性

1.擴展屬性

除了給類添加功能擴展函數外,你還可以給類定義擴展屬性,給String類添加一個擴展屬性,這個擴展屬性可以統計字符串里有多少個元音字母。

image.png

2.可空類擴展

你也可以定義擴展函數用于可空類型,在可空類型上定義擴展函數,你就可以直接在擴展函數內部解決可能出現的空值問題。

image.png

運行結果:abc

3.infix關鍵字

上個例子中用到了infix關鍵字,該關鍵字適用于有單個參數的擴展函數和類函數,可以讓你以更簡潔的語法調用函數,如果一個函數定義使用了infix關鍵字,那么調用它時,調用者和函數之間的點以及參數的一對括號都可以不要。

4.定義擴展文件

擴展函數需要在多個文件里面使用,可以將它定義在單獨的文件中,然后import。

定義擴展函數:

image.png

使用擴展函數:

image.png

5.重命名擴展

有時候,你想使用一個擴展或一個類,但他的名字不合你意。

image.png

6.Kotlin標準庫中的擴展

Kotlin標準庫提供的很多功能都是通過擴展函數和擴展屬性來實現的,包含類擴展的標準庫文件通常都是以類名加S后綴來命名的,例如:Sequences.kt,Ranges.kt,Maps.kt

三.DSL

1.帶接收者(接收者就是函數的調用者)的函數字面量

apply函數是如何做到支持接收者對象的隱式調用的。

image.png

首先,擴展函數里自帶類調用者的this的隱式調用,apply函數的入參是block: T.() -> Unit,這是一個匿名函數,返回值類型為Unit(無返回值類型),而T.()是什么鬼?我們上邊有介紹泛型擴展函數,比如fun <T> T.easyPrint(): Unit,所以T.() -> Unit就是匿名的泛型擴展函數,這樣做就是為了讓所有類型都可以使用apply這個函數,而且在lambda表達式中還持有調用類的this,這樣就可以在lambda表達式中直接調用該類的方法

2.DSL

使用這樣的編程范式,就可以寫出業界知名的“領域特定語言”(DSL),一種API編程范式,暴露調用者的函數和特性,以便于使用你定義的lambda表達式來讀取和配置它們。

四.函數式編程

一個函數式應用通常由三大類函數構成:變換transform、過濾filter、合并combine。每類函數都針對集合數據類型設計,目標是產生一個最終結果。函數式編程用到的函數生來都是可組合的,也就是說,你可以組合多個簡單函數來構建復雜的計算行為。

1.變換

變換是函數式編程的第一大類函數,變換函數會遍歷集合內容,用一個以值參形式傳入的變換器函數,變換每一個元素,然后返回包含已修改的集合給鏈上的其他函數。
最常用的兩個變換函數是map和flatMap。

2.map

map變換函數會遍歷接收者集合,讓變換器函數作用于集合里的各個元素,返回結果是包含已修改元素的集合,會作為鏈上下一個函數的輸入。
map函數定義如下:
public inline fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R>

image.png

運行結果:
[zebra, giraffe, elephant, rat]
[A baby zebra, with the cutest little tail ever!, A baby giraffe, with the cutest little tail ever!, A baby elephant, with the cutest little tail ever!, A baby rat, with the cutest little tail ever!]

可以看到,原始集合沒有被修改,map變換函數和你定義的變換器函數做完事情后,返回的是一個新集合,這樣,變量就不用變來變去了。
事實上,函數式編程范式支持的設計理念就是不可變數據的副本在鏈上的函數間傳遞。
map返回的集合中的元素個數和輸入集合必須一樣,不過,返回的新集合里的元素可以是不同類型的。

image.png

運行結果:
[zebra, giraffe, elephant, rat]
[5, 7, 8, 3]

3.flatMap

flatMap函數操作一個集合的集合,將其中多個集合中的元素合并后返回一個包含所有元素的單一集合。

image.png

運行結果:
[1, 2, 3, 4, 5, 6]

4.過濾

過濾是函數式編程的第二大類函數,過濾函數接受一個predicate函數,用它按給定條件檢查接收者集合里的元素并給出true或false的判定。如果predicate函數返回true,受檢元素就會添加到過濾函數返回的新集合里。如果predicate函數返回false,那么受檢元素就被移出新集合。

5.filter

filter函數定義如下:
public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T>

例1:過濾集合中元素含有“J”字母的元素。

image.png

運行結果:
[Jack, Jimmy]

例2:filter過濾函數接收一個predicate函數,在flatMap遍歷他的輸入集合中的所有元素時,filter函數會讓predicate函數按過濾條件,將符合條件的元素都放入它返回的新集合里。最后,flatMap會把變換器函數返回的子集合合并在一個新集合里。

image.png

運行結果:
[red apple, red fish]

例3:找素數,除了1和它本身,不能被任何數整除的數。僅使用了幾個簡單函數,我們就解決了找素數這個比較復雜的問題,這就是函數式編程的獨特魅力:每個函數做一點,組合起來就能干大事。

image.png

6.合并

合并是函數式編程的第三大類函數,合并函數能將不同的集合合并成一個新集合,這和調用者是包含集合的集合的flatMap函數不同。

7.zip

zip合并函數來合并兩個集合,返回一個包含鍵值對的新集合。

image.png

運行結果:
{Jack=large, Jason=x-large, Tommy=medium}

8.fold

另一個可以用來合并值的合并類函數是fold,這個合并函數接收一個初始累加器值,隨后會根據匿名函數的結果更新。

image.png

運行結果:30

9.序列

List、Set、Map集合類型,這幾個集合類型統稱為及早集合(eager collection),這些集合的任何一個實例在創建后,他要包含的元素都會被加入并允許你訪問。對應及早集合,Kotlin還有另外一類集合:惰性集合(lazy collection),類似于類的惰性初始化,惰性集合類型的性能表現優異,尤其是用于包含大量元素的集合時,因為集合元素是按需產生的。
Kotlin有個內置惰性集合類型叫序列(Sequence),序列不會索引排序它的內容,也不記錄元素數目,事實上,在使用一個序列時,序列里的值可能有無限多,因為某個數據源能產生無限多個元素。

10.generateSequence

針對某個序列,你可能會定義一個只要序列有新值產生就被調用一下的函數,這樣的函數叫迭代器函數,要定義一個序列和它的迭代器,你可以使用Kotlin的序列構造函數generateSequence,generateSequence函數接收一個初始值作為序列的起步值,在用generateSequence定義的序列上調用一個函數時,generateSequence函數會調用你置頂的迭代器函數,決定下一個要產生的值。
惰性集合究竟有什么用呢?為什么要用它而不是List集合呢?假設你想產生頭1000個素數。

image.png

運行結果:669

這樣的代碼實現表明,你不知道該檢查多少個數才能得到整1000個素數,所以用5000這個預估數。但事實上5000個數遠遠不夠,只能找出669個素數。

image.png

使用如上這種方式(generateSequence)就可以得到1000個素數了,運行結果:1000

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,797評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,179評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,628評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,642評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,444評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,948評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,040評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,185評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,717評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,794評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,418評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,414評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,750評論 2 370

推薦閱讀更多精彩內容