全網最全面的由淺到深的Kotlin基礎教程(七)

前言

本篇文章接著上一篇文章全網最全面的由淺到深的Kotlin基礎教程(六)繼續進階學習kotlin,建議看完上一篇文章,再來看本篇文章。本篇主要分析一些常用的kotlin函數源碼,以及用kotlin簡單實現Rxjava的幾個操作符。堅持將kotlin系列的前6篇看完的,那么恭喜你再將這最后一篇看完,你就成為一名合格的kotlin開發者了。

1. kotlin標準函數源碼解析

1.1 apply

源碼及分析注釋如下:

/*
public 訪問修飾符
inline fun 內聯函數
<T> 泛型定義
T.apply 泛型T的擴展函數
(block: T.() -> Unit) 輸入參數為一個名為block的lambda表達式,其中T.() 是一個泛型擴展函數,這種寫法使得lambda表達式持有T的this對象,當做固定寫法來理解即可。
: T apply函數的返回類型仍然為T
*/
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {//契約函數,暫時不用關注,可以不寫的contract {callsInPlace(block, InvocationKind.EXACTLY_ONCE)}//執行lambda表達式的邏輯block()//最終仍然返回T對象本身return this
}

1.2 also

與apply函數的意義一樣,只是also函數的lambda表達式持有it,而apply函數的的lambda表達式持有this,源碼及分析注釋如下:

/*
public 訪問修飾符
inline fun 內聯函數
<T> 泛型定義
T.also 為泛型T定義擴展函數also
block: (T) -> Unit also函數中傳入的參數是lambda表達式,該lambda表達式有一個T泛型的參數,返回值為空
: T also函數的最終返回值仍然為T泛型
*/
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.also(block: (T) -> Unit): T {contract {callsInPlace(block, InvocationKind.EXACTLY_ONCE)}//執行lambda表達式,將T泛型的this對象傳入block函數,因此block通過it持有T泛型的this對象block(this)//不管你上面的lambda表達式干了啥,最終仍然返回T泛型對象本身return this
}

1.3 run

源碼及分析注釋如下:

/*
public 訪問修飾符
inline fun 內聯函數
<T, R> 定義兩個泛型
T.run 為T泛型定義擴展函數run
block: T.() -> R run函數有一個參數是lambda表達式,該lambda表達式通過T泛型擴展函數,擁有T泛型的this對象,返回類型為R
: R run函數的返回類型是R泛型
*/
@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R {contract {callsInPlace(block, InvocationKind.EXACTLY_ONCE)}//執行lambda表達式//因為lambda表達式擁有T泛型的this對象//因為lambda表達式返回的是R,因此返回值由lambda表達式的最后一行決定//return this.block()  //this可以省略return block()
}

1.4 let

與run函數的意義一樣,只是let函數的lambda表達式持有it,而run函數的的lambda表達式持有this,源碼及分析注釋如下:

/*
public 訪問修飾符
inline fun 內聯函數
<T, R> 定義兩個泛型
T.let 為T泛型定義擴展函數let
block: (T) -> R lambda表達式參數,輸入T泛型(lambda表達式如果只有一個參數,則參數名默認為it),輸出為R泛型
: R 最終返回的類型是R泛型
*/
@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {contract {callsInPlace(block, InvocationKind.EXACTLY_ONCE)}//執行lambda表達式//因為lambda表達式傳入的是this,因此lambda表達式通過it持有T泛型的對象//因為lambda表達式輸入的T,返回的是R,因此返回值由lambda表達式的最后一行決定return block(this)
}

1.5 with

與run函數的意義一樣,只是調用方式不一樣而已,run是泛型擴展函數,而with是普通的函數,源碼及分析注釋如下:

/*
public 訪問修飾符
inline fun 內聯函數
<T, R> 定義兩個泛型
with(receiver: T, block: T.() -> R) 這是一個普通的函數,并非擴展函數,包括兩個參數,第一個T泛型的參數,第二個是lambda表達式,lambda表達式通過T泛型擴展函數,擁有T泛型的this對象,返回類型為R
: R 最終返回類型是R
*/
@kotlin.internal.InlineOnly
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {contract {callsInPlace(block, InvocationKind.EXACTLY_ONCE)}//block的最后一行是返回值return receiver.block()
}

1.6 takeIf

源碼及分析注釋如下:

/*
public 訪問修飾符
inline fun 內聯函數
<T> 泛型定義
T.takeIf 為泛型T定義擴展函數takeIf
predicate: (T) -> Boolean takeIf函數中傳入的參數是lambda表達式,該lambda表達式有一個T泛型的參數,返回值為Boolean
: T? takeIf函數最終返回的可空的T泛型類型
*/
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? {contract {callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)}//如果lambda表達式返回true,則takeIf返回T泛型this對象本身,否則返回nullreturn if (predicate(this)) this else null
}

1.7 takeUnless

與takeIf的意義相反,源碼及分析注釋如下:

/*
public 訪問修飾符
inline fun 內聯函數
<T> 泛型定義
T.takeUnless 為泛型T定義擴展函數takeUnless
predicate: (T) -> Boolean takeUnless函數中傳入的參數是lambda表達式,該lambda表達式有一個T泛型的參數,返回值為Boolean
: T? takeIf函數最終返回的可空的T泛型類型
*/
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? {contract {callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)}//如果lambda表達式返回false,則takeUnless返回T泛型this對象本身,否則返回nullreturn if (!predicate(this)) this else null
}

2. kotlin轉換函數源碼解析

2.1 map

  • 功能介紹:
    源碼及分析注釋如下:把lambda表達式的返回結果(最后一行),添加到新的集合destination中,并返回。
  • 示例代碼:
fun main() {var list = listOf("A", "B", "C")list.map {"[$it]"}.map {"[$it, len=${it.length}]"}.map {print("$it ")}}

運行結果如下:
在這里插入圖片描述

  • 源碼分析:
//Iterable類的擴展函數map
public inline fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R> {//將創建的List數組和lambda表達式傳入mapTo函數//return this.mapTo(ArrayList<R>(collectionSizeOrDefault(10)), transform) //this可以省略return mapTo(ArrayList<R>(collectionSizeOrDefault(10)), transform)
}public inline fun <T, R, C : MutableCollection<in R>> Iterable<T>.mapTo(destination: C, transform: (T) -> R): C {//遍歷調用map的List對象for (item in this)//將原有的list值傳遞給lambda表達式transform處理,并存入新的集合destinationdestination.add(transform(item))return destination //返回新的集合
}

2.2 flatmap

  • 功能介紹:
    lambda表達式的返回結果是一個集合,把lambda表達式的返回集合,通過addAll函數鋪開,并添加到新的集合destination中,最后返回新的集合。
  • 示例代碼:
fun main() {var list = listOf("A", "B", "C")list.flatMap {listOf("[$it ,", "$it]")}.map {print("$it ")}
}

運行結果如下:
在這里插入圖片描述

  • 源碼分析:
//Iterable類的擴展函數
public inline fun <T, R> Iterable<T>.flatMap(transform: (T) -> Iterable<R>): List<R> {return flatMapTo(ArrayList<R>(), transform)
}public inline fun <T, R, C : MutableCollection<in R>> Iterable<T>.flatMapTo(destination: C, transform: (T) -> Iterable<R>): C {//遍歷flatMap調用者的集合元素for (element in this) {//lambda表達式返回一個集合val list = transform(element)//將lambda表達式返回的集合添加到新的集合destination中destination.addAll(list)}//返回新的集合return destination
}

2.3 filter

  • 功能介紹:
    如果lambda表達式的返回值為true,則遍歷當前元素,加入新的字符串構造器StringBuilder destination,否則,不加入新的字符串。最后返回新的字符串
  • 示例代碼:
fun main() {var list = listOf("A", "B", "C")list.map {//過濾list中為A的元素it.filter { it ->println("$it filter")it == 'A'}}.map {print("$it  ")}
}

運行結果如下:
在這里插入圖片描述

  • 源碼分析:
//String類的擴展函數filter
public inline fun String.filter(predicate: (Char) -> Boolean): String {return filterTo(StringBuilder(), predicate).toString()
}public inline fun <C : Appendable> CharSequence.filterTo(destination: C, predicate: (Char) -> Boolean): C {//遍歷filter調用者的字符串的所有字符for (index in 0 until length) {val element = get(index)//如果這個字符傳入lambda表達式predicate的返回結果為true,則添加到StringBuilder中if (predicate(element)) destination.append(element)}//返回新的字符串StringBuilderreturn destination 
}

2.4 zip

  • 功能介紹
    通過lambda表達式將第一個集合和另一個集合,以Pair的形式添加到新的集合中,并返回
  • 示例代碼
fun main() {val name = listOf("sun", "mekeater")val age = listOf(18,19)var zip = name.zip(age)//將zip轉為Map遍歷zip.toMap().forEach{println("name=${it.key},age=${it.value}")}
}

運行結果如下:
在這里插入圖片描述

  • 源碼分析
//Iterable類的擴展函數zip
public infix fun <T, R> Iterable<T>.zip(other: Iterable<R>): List<Pair<T, R>> {return zip(other) { t1, t2 -> t1 to t2 }
}
//中綴表達式,將A和B組合成一個Pair對象
public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)public inline fun <T, R, V> Iterable<T>.zip(other: Iterable<R>, transform: (a: T, b: R) -> V): List<V> {//zip函數調用者對象遍歷器val first = iterator()//zip函數中傳入的另一個集合對象遍歷器val second = other.iterator()//新建集合val list = ArrayList<V>(minOf(collectionSizeOrDefault(10), other.collectionSizeOrDefault(10)))//遍歷兩個集合while (first.hasNext() && second.hasNext()) {//將兩個集合的元素傳給lambda表達式transform//上面可以看到transform的實現,是將兩個參數組合為一個Pair//將Pair對象添加到新的集合list中list.add(transform(first.next(), second.next()))}//返回新的集合return list
}

3. 函數式編程理解

kotlin是函數式編程語言,函數式編程語言的代碼更簡潔直觀(如果你會的話,真的很容易讀懂,但如果不會,那真的又一一點看不懂,相信認真看了我kotlin這7篇文章的,到這兒應該都能輕松的讀懂下的kotlin代碼),我們以一段示例代碼來對比下,函數式編程和普通編程方式的區別。

如下一段kotlin函數式編程,用普通的java代碼寫,需要寫一堆才能實現

fun main() {val name = listOf("sun", "mekeater")val age = listOf(18,19)//name.zip(age) 將name和age兩個集合組成鍵值對集合//.toMap() 將上一步鍵值對集合轉為Map集合//.map { "name:${it.key},age:${it.value}"} 遍歷上一步Map集合中的元素,按照"name:${it.key},age:${it.value}"格式進行組合//.map { println(it) 遍歷上一步Map集合中的元素,進行打印輸出name.zip(age).toMap().map { "name:${it.key},age:${it.value}"}.map { println(it) }
}

對上面的kotlin函數式編程寫的代碼用java實現,示例代碼如下:

public class Kt43_java {public static void main(String[] args) {List<String> names=new ArrayList<>();names.add("sun");names.add("mekeater");List<Integer> ages =new ArrayList<>();ages.add(18);ages.add(19);Map<String, Integer> map =new LinkedHashMap<>();for (int i = 0; i < names.size(); i++) {map.put(names.get(i), ages.get(i));}List<String> newList=new ArrayList<>();for (Map.Entry<String, Integer> stringIntegerEntry : map.entrySet()) {newList.add("name:"+stringIntegerEntry.getKey()+" age:"+stringIntegerEntry.getValue());}for (String s : newList) {System.out.println(s);}}}

kotlin的一行代碼,java要用一堆才能實現,kotlin代碼的簡潔程度不言而喻!

4. 手寫RxJava

采用kotlin代碼實現RxJava的create,map,observer三個操作符,通過這個示例,真的更能感受到kotlin代碼的簡潔程度令人發指,哈哈哈

fun main() {create {"mekeater"}.map {"[$this]"}.map {"==$this=="}.observer {println(this)}
}
class RxjavaCoreClass<T>(val item:T)//lambda對輸入進行處理
inline fun<I> RxjavaCoreClass<I>.observer(observerAction:I.()->Unit):Unit = observerAction(item)//對于輸入進行處理,轉換為輸出給到RxjavaCoreClass
inline fun<I,O> RxjavaCoreClass<I>.map(mapAction:I.()->O) = RxjavaCoreClass(mapAction(item))//create操作符只是將用戶最后一行的輸入原樣輸出給RxjavaCoreClass,起到一個輸入參數的作用
inline fun<O> create(action:()->O) = RxjavaCoreClass(action())

運行結果如下:
在這里插入圖片描述

5. 結語

自此,我們kotlin入門教程系列就講完了,一共7篇文章,如果您能從第一篇看到第七篇,相信你已經很好的掌握kotlin語言了,至少能夠看懂kotlin代碼,能夠自己上手寫kotlin代碼了。

那么,感謝你來我的博客世界,聽我講kotlin的故事,這個故事該說再見了,希望在下個新故事,還能遇到你,886~

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/13432.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/13432.shtml
英文地址,請注明出處:http://en.pswp.cn/web/13432.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

卡梅德生物噬菌體展示多肽庫

噬菌體展示多肽文庫在新藥發現領域展現出巨大的潛力和應用價值。卡梅德生物的噬菌體展示肽庫通過將大量肽序列插入到噬菌體基因組中&#xff0c;并在噬菌體表面展示這些肽&#xff0c;構建了一個多樣性的肽庫。 在新藥發現過程中&#xff0c;噬菌體展示多肽文庫可以用于篩選具有…

全志A133 android10 調試vibrator震動馬達

一&#xff0c;前提條件 全志使用的馬達配置為上電震動&#xff0c;下電停止&#xff0c;需測試硬件是否正常。馬達供電最好為獨立供電&#xff0c;避免干擾。 二&#xff0c;適配步驟 1. dts中增加馬達配置 motor_para {compatible "allwinner,sunxi-vibrator";…

java工廠模式示例代碼

工廠模式是一種創建型設計模式&#xff0c;它提供了一種將對象的創建與使用分離的方法。在工廠模式中&#xff0c;我們通過工廠類來創建對象&#xff0c;而不是直接在客戶端代碼中使用 new 關鍵字來創建對象。以下是一個簡單的Java工廠模式示例代碼&#xff1a; 產品接口 首先…

BGP實驗:聯邦和發射器實驗

BGP實驗&#xff1a;聯邦和發射器實驗 一、實驗拓撲 二、實驗要求及分析 實驗要求&#xff1a; 1、AS1存在兩個環回&#xff0c;一個地址為192.168.1.0/24&#xff0c;該地址不能再任何協議中宣告&#xff1b; ? AS3存在兩個環回&#xff0c;一個地址為192.168.2.0/24&…

解決ModuleNotFoundError: No module named ‘open_clip‘問題

在使用stable diffusion大模型時&#xff0c;添加一些模型后啟動df頁面報錯&#xff1a;ModuleNotFoundError: No module named open_clip 使用 pip install open_clip命令下載失敗&#xff0c;報錯&#xff1a; Looking in indexes: https://mirrors.aliyun.com/pypi/simple…

Redis【B站面試題】

前言 2023-07-27 22:44:59 出自B站 灰灰的Java面試 Redis Redis為什么快&#xff1f; 1.純內存KV操作 Redis的操作都是基于內存的&#xff0c;CPU不是 Redis性能瓶頸,&#xff0c;Redis的瓶頸是機器內存和網絡帶寬。 在計算機的世界中&#xff0c;CPU的速度是遠大于內存的速…

Java技術深度解析:高級面試問題與精粹答案(二)

Java 面試問題及答案 1. 什么是Java的垃圾回收機制&#xff1f;它是如何工作的&#xff1f; 答案&#xff1a; Java的垃圾回收機制&#xff08;Garbage Collection&#xff0c;GC&#xff09;是Java運行時環境&#xff08;JRE&#xff09;中的一個功能&#xff0c;用于自動管…

[CocosCreator]Android的增加AndroidX的動態權限

歡迎喜歡或者從事CocosCreator開發的小伙伴請加入我的大家庭CocosCreator游戲開發Q群:26855530 1.首先增加你需要申請的權限,修改:AndroidManifest.xml: <?xml version"1.0" encoding"utf-8"?> <manifest xmlns:android"http://schemas…

深度學習之基于TensorFlow人臉表情識別

歡迎大家點贊、收藏、關注、評論啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代碼。 文章目錄 一項目簡介 二、功能三、系統四. 總結 一項目簡介 一、項目背景 人臉表情識別是計算機視覺領域的重要研究方向之一&#xff0c;它在人機交互、情感分析、安全監控等領…

代碼隨想錄35期Day48-Java

Day48題目 LeetCode121買股票的最佳時機1 核心思想:可以使用貪心,選擇左邊最小的和右邊最大的,也可以動態規劃,需要保存是否持有股票的狀態,dp[i][0]表示第i天,不持有股票,dp[i][1]表示第i天持有 class Solution {public int maxProfit(int[] prices) {int[][] dp new int[…

python中異步io簡單樣例

目錄 一、異步IO簡單說明 二、代碼樣例 一、異步IO簡單說明 當進行異步 IO 操作時&#xff0c;程序不會因為等待 IO 操作完成而阻塞&#xff0c;而是可以在等待過程中繼續執行其他任務&#xff0c;從而提高了程序的并發性能和響應速度。這是因為異步 IO 操作利用了操作系統底層…

Java 變量和作用域:理解變量的聲明、初始化及其作用域

在Java編程語言中&#xff0c;變量和作用域是兩個核心概念。理解變量的聲明、初始化以及它們的作用域對于編寫健壯且高效的代碼至關重要。 變量的聲明與初始化 變量的聲明 在Java中&#xff0c;變量的聲明指的是定義變量的名稱和類型。在Java中&#xff0c;變量聲明的一般語…

ESP32開發——關于ESP32TimerInterrupt庫的例程分析

最近在學習嵌入式開發的內容&#xff0c;正好有一個開發任務涉及到對于定時器中斷的使用&#xff0c;今天正好找到了這個相關的庫&#xff1a;ESP32TimerInterrupt ESP32TimerInterrupt庫的下載鏈接&#xff08;適用于Arduino IDE&#xff09; 進入到這個地址后直接下載該庫的…

ubuntu使用記錄——如何使用wireshark網絡抓包工具進行檢測速騰激光雷達的ip和端口號

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 文章目錄 前言wireshark網絡抓包工具1.wireshark的安裝2.wireshark的使用3.更改雷達ip 總結 前言 Wireshark是一款備受贊譽的開源網絡協議分析軟件&#xff0c;其功能之強大…

簡述MyBatis中#{}引用和${}引用的區別

各位大佬光臨寒舍&#xff0c;希望各位能賞臉給個三連&#xff0c;謝謝各位大佬了&#xff01;&#xff01;&#xff01; 目錄 1.有無預編譯 優點 缺點 2.SQL執行的快慢 3.能否被SQL注入 4.參數輸入方式 5.總結 1.有無預編譯 #{}是有預編譯的而${}是沒有預編譯的&…

LiveGBS流媒體平臺GB/T28181用戶手冊-服務器概覽:通道信息、負載信息、CPU使用、存儲使用、帶寬使用(Mbps)、內存使用

LiveGBS用戶手冊-服務器概覽&#xff1a;通道信息、負載信息、CPU使用、存儲使用、帶寬使用&#xff08;Mbps&#xff09;、內存使用 1、服務器概覽1.1、通道信息1.2、負載信息1.2.1、信息說明1.2.2、會話列表 1.3、CPU使用1.4、存儲使用1.5、帶寬使用&#xff08;Mbps&#xf…

15:00面試,15:08出來,面試問的有點變態。。。。

&#x1f345; 視頻學習&#xff1a;文末有免費的配套視頻可觀看 &#x1f345; 點擊文末小卡片&#xff0c;免費獲取軟件測試全套資料&#xff0c;資料在手&#xff0c;漲薪更快 從小廠出來&#xff0c;沒想到在另一家公司又寄了。 到這家公司開始上班&#xff0c;加班是每天…

C語言查漏補缺學習【精簡版】

1.scanf函數 scanf&#xff08;"%d %d"&#xff09;//1 2 scanf("price%d %d") //price1 2 // 意思就是scanf函數中&#xff0c;格式是什么&#xff0c;就要對應的輸入什么&#xff0c;不然讀取不到輸入的變量2.常量&#xff1a;const int AMOUNT 100; …

github下載代碼

clone代碼 git clone https://<your_token>github.com//.git 設置URL git remote set-url origin https://<your_token>github.com//.git 設置github本地ip https://juejin.cn/post/7350880189836918820?searchId202405191828091DB64DD476DF0AEC7442