Overview of Content
本文將全面探討 Kotlin Lambda 編程的相關主題,首先介紹編程基礎,隨後深入解析 Kotlin Lambda
的概念和使用方法。透過逐步引導,我們將探討 Lambda 在 Kotlin 中的具體應用,並深入研究其在 ByteCode
層面的實現。
此外,我們將探討 Kotlin 如何與 Java 函數式相互操作,為讀者提供跨語言編程的實際示例。另一方面,我們也將深入研究數組(Array
)和集合(Collections
),並介紹 Kotlin 強大的集合函數式 API,包括 map
、filter
、any
和 all
等功能,以協助讀者更深入理解 Kotlin 編程的精髓。
以下參考,第一行代碼 (第三版), Kotlin 進階實戰
如有引用參考本文章請詳註出處,感謝 🙂
Kotlin Lambda 編程介紹
Kotlin 從第一版開始就支持 Lambda 編程,並且 Lambda 在 Kotlin 的功能極為強大,這裡會先以基礎為例,還有其他高階函數、DSL...等等
Lambda & Kotlin: Step by Setp
● 這裡的重點是學習 函數式 API 的語法結構,就是 Lambda 表達式在 Kotlin 中的運用,Lambda 可以想做把函式當作參數傳入
先來看看 Lambda 表達式在 Kotlin 中的語法
/**
* 1. 最外面大括號
* 2. 參數列表的尾端使用 `->` 符號
* 3. 並且 Lambda 最後一行為返回的數值
*/
{參數名1: 參數類型, 參數名2: 參數類型 -> 函數體}
以下在集合中求取最大長度的字串,第一個是傳統的方式,第二個是 Lambda 的使用,之後皆是推導 (不然還真的看不懂)
val list = listOf<String>("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon")
fun main() {
customCompare()
kotlinLambda()
kotlinLambda1()
kotlinLambda2()
kotlinLambda3()
kotlinLambda4()
kotlinLambda5()
}
/**
* 傳統比對方式
*/
fun customCompare() {
var maxLength = ""
for (i in list) {
if (i.length > maxLength.length) {
maxLength = i
}
}
println("custom Max Length is: $maxLength")
}
/**
* Lambda...看沒有 ? 往下看
*/
fun kotlinLambda() {
// maxBy 函數原型
// public inline fun <T, R : Comparable<R>> Iterable<T>.maxBy(selector: (T) -> R): T? {...}
val maxLength = list.maxBy { it.length }
println("lambda Max Length is: $maxLength")
}
/**
* 第一步
* 依照 Lambda 表達式先創建
*/
fun kotlinLambda1() {
val lambda = {i : String -> i.length}
println("Lambda - 1, lambda: $lambda") // 1
val maxLength = list.maxBy(lambda)
println("Lambda - 1: $maxLength")
}
/**
* 第二步
* 不須定義一個 Lambda 變量
*/
fun kotlinLambda2() {
val maxLength = list.maxBy( {i: String -> i.length} ) // 2
println("Lambda - 2: $maxLength")
}
/**
* 第三步
* 當最後一個參數為 Lambda 時可以將 Lambda 移到 () 外面
*
* 第四步
* 若 Lambda 為唯一一個參數時可以移除 ()
*/
fun kotlinLambda3() {
// val maxLength = list.maxBy() { i: String -> i.length} // 3
val maxLength = list.maxBy { i: String -> i.length} // 4
println("Lambda - 3/4: $maxLength")
}
/**
* 第五步
* 由於 Kotlin 有自動推倒機制,所以可以省略參數的 :String 類型
* (參數列表中大多數都不須宣告類型)
*/
fun kotlinLambda4() {
val maxLength = list.maxBy { i -> i.length} // 5
println("Lambda - 5: $maxLength")
}
/**
* 第六步
* 若 "只有一個參數" 則可以不用聲明參數,可以使用關鍵字 it
*/
fun kotlinLambda5() {
val maxLength = list.maxBy { it.length} // 6
println("Lambda - 6: $maxLength")
}
● 以下整理重點步驟
A. Lambda 原型
list.maxBy{(i: String -> i.length)}
B. 當最後一個參數為 Lambda 時可以將 Lambda 移到 ()
外面
list.maxBy() {i: String -> i.length}
C. 若 Lambda 為唯一一個參數時可以移除小括號 ()
list.maxBy{i: String -> i.length}
D. 由於 Kotlin 有自動推倒機制,所以可以省略參數的類型
list.maxBy{i -> i.length}
E. 若「只有一個參數」則可以不用聲明參數,可以使用關鍵字 it
list.maxBy{it.length}
--實作結果--
● 返回一個函數:一般來講程式語言只能返回基礎類型,但是函數式編程的特點就在於,可以將「函數作為值」返回
enum class ControlType { ADD, SUB } fun getLambda(type : ControlType) : (Int, Int) -> Int { return when(type) { ControlType.ADD -> { a, b -> a + b} ControlType.SUB -> { a, b -> a - b} } } fun main() { val lambda = getLambda(ControlType.ADD) println("Add res: ${lambda(1, 2)}") val lambda2 = getLambda(ControlType.SUB) println("Sub res: ${lambda2(10, 1)}") }
Lambda
同時也可以作為一個返回值返回
● Lambda 中「未」使用到的參數建議使用
_
取代其參數名稱,這可以增加程式的可讀性!
解析 Kotlin Lambda:ByteCode
● 現在換個角度來看看 Lambda 表達式:先撰寫 Kotlin 一個帶有 Lambda 函數式的程式,再將其編譯,最後查看 .class
文件
fun main() {
val sum = {
x: Int, y : Int -> x + y
}
println(sum(1, 2))
}
● 透過 IDE 提供的
kotlin Bytecode
工具查看該文件的.class
檔案
A. 呼叫 sum
Lambda 函數:可以看到它調用了 Function2#invoke 方法(Function2
代表帶兩個參數的函數)
B. 而 sum
函數則實作了 Function2 介面
● Lambda 會實現
Function<N>
接口,N 的大小由參數決定,接收參數越多則 N 越大●
inline
內聯函數 則 不需實現 Function 接口!
Kotlin 調用 Java 函數式
● Kotlin 中調用 Java 也可以使用函數式 (Lambda),但須有一些條件限制 (其實也就是 Java 中實現 Lambda 的方法),Java 抽象接口只能有單一個實現方法
// Runnable 原碼
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
// Java 使用匿名內部類調用
public class JavaThread {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello Java Thread");
}
}).start();
}
}
從上面可以看出來 Runnable
這個介面內部只有一個方法(run
),符合 Kotlin 調用函數式 API 的規定
● 匿名內部類
匿名內部類必須使用關鍵字
object
,因為 Kotlin 捨棄了new
關鍵字,所以在創建匿名內部類時要使用object
fun main() {
javaThread1()
javaThread2()
javaThread3()
javaThread4()
}
/**
* 匿名內部類必須使用關鍵字 "object"
* 因為 Kotlin 捨棄了 new 關鍵字,所以在創建匿名內部類時要使用 object
*/
fun javaThread1() {
Thread(object : Runnable {
override fun run() {
println("Kotlin use Runnable - 1")
}
}).start()
}
/**
* object 可以提醒未實現方法,但 Runnable 就只有一個實現方法而已
* 所以可以省略 object & run 方法 (因為 Kotlin 可以自動推導知道
*/
fun javaThread2() {
Thread(Runnable {
println("Kotlin use Runnable - 2")
}).start()
}
/**
* 如果 Java 方法的參數列表中只有一個 Java 抽象接口,則 Kotlin 可以自動推倒
* 所以可以省略 Runnable
*/
fun javaThread3() {
Thread({
println("Kotlin use Runnable - 3")
}).start()
}
/**
* 黨參數 Lambda 表達式為最後一個參數時可以省略 (),只留下 {} 大括號
*/
fun javaThread4() {
Thread {
println("Kotlin use Runnable - 4")
}.start()
}
--實作結果--
Array 數組
/**
* arrayOf 是內建函數
*/
fun main() {
val array = arrayOf(1, 3, 5, 7, 9)
for (i in array) {
println("array: $i")
}
// 原型: public inline constructor(size: Int, init: (Int) -> T)
val lambda = {i : Int -> i + 10} // i 從 0 開始
val array2 = Array(5, lambda)
for (i in array2) {
println("lambda i : $i")
}
/**
* 省略方式
* 1. 單一參數可以使用 it
* 2. lambda 最為最後一個參數可以一至 (外)
*/
val array3 = Array(5) {it + 10} // i 從 0 開始
for (i in array3) {
println("lambda i --- 2 : $i")
}
}
-實作結果--
Collections 集合
● 在 Java 中的集合有 List、Set、Map,它們的實現如下 (列舉出常用的)
集合 | 實現 |
---|---|
List | ArrayList、LinkedList |
Set | HashSet |
Map | HashMap |
● Kotlin 對於集合的創建有簡化的內置函數
集合內置函數 | 說明特性 |
---|---|
listOf | 初始化時就必須決定集合內容,並且該集合 內容不可更改的 |
mutableListOf | mutable 關鍵字如同 C++ 的關鍵字,說明該 集合內容可以改變 |
setOf | 如同 list 集合,同樣是不可更改的集合 |
mutableSetOf | 可以更改的 Set 集合 |
mapOf | 不可改變的 map 集合,Kotlin 不建議使用 put、get,改用指標下定的方式 |
mutableMapOf | 可更改的 Map 集合,並使用 infix 函數 |
fun main() {
customArray()
kotlinList()
kotlinListChange()
kotlinSet()
kotlinSetChange()
customMap()
kotlinIndex()
kotlinMap()
kotlinMapChange()
forListWithIndex()
}
/**
* Kotlin 可以實現如同 Java 的 List
*/
fun customArray() {
val list = ArrayList<Int>()
list.add(123)
list.add(456)
list.add(789)
println("custom list: $list")
}
/**
* Kotlin 有專屬的 "內置" 函數 listOf (不可更改 !!!
* 用來簡化 list 的初始化創建
*/
fun kotlinList() {
val list = listOf<String>("Apple, Banana, Car")
// list[0] = "Bpple"; Error: listOf 內容不可更改,內部元素都是 val
// list.add(); 也不可以新增元素
println("listOf: $list")
}
/**
* mutableListOf 是一個可以更改內容的集合
*/
fun kotlinListChange() {
val list = mutableListOf<String>("Hello", "World")
println("mutableListOf before change: $list")
list[1] = "Kotlin" // Okay: mutableListOf 內部元素可以更改
list.add("Go to Lean~") // 也可以新增內容
println("mutableListOf after change: $list")
}
fun kotlinSet() {
val set = setOf<String>("AAA", "BBB", "CCC")
for ( i in set) {
println("setOf element: $i")
}
}
fun kotlinSetChange() {
val set = mutableSetOf("DDD")
set.add("EEE")
set.add("FFF")
for ( i in set) {
println("mutableSetOf element: $i")
}
}
fun customMap() {
val map = HashMap<String, Long>()
map.put("Alien", 9527L)
map.put("Pan", 3344L)
map.put("Kyle", 5566L)
println("custom Map: $map")
// for (i in map) { // 遍歷 Okay
// println("key: ${i.key}, value: ${i.value}")
// }
}
fun kotlinIndex() {
val map = HashMap<String, Long>()
map["Alien"] = 9527L
map["Pan"] = 3344L
map["Kyle"] = 5566L
println("custom Map use index: $map")
}
/**
* to 是一個 index 函數
*/
fun kotlinMap() {
val map = mapOf<Char, Int>('A' to 1, 'B' to 2, 'C' to 3)
// map['A'] = 10 Error 不可賦值
println("mapOf: $map")
}
fun kotlinMapChange() {
val map = mutableMapOf<Char, Int>('C' to 4)
map['D'] = 5
map['E'] = 6
println("mutableMapOf: $map")
}
/**
* 要取得 index 必須使用 集合.indices
*/
fun forListWithIndex() {
val list = listOf<Char>('A', 'B', 'C')
list.forEach { a -> println(a) }
for (index in list.indices) {
println("index: $index, value: ${list[index]}")
}
}
--實作結果--
集合函數式 API:map、filter、any、all
● 以下會提及幾個常用的集合 API,其接收參數就是使用 Lambda
A. 集合的 map
函數:它會將集合中的每一個元素都套用傳入的 Lambda,並產生一個新的值,最終生成新的集合
fun main() {
val list = listOf(100, 200, 300, 400, 500)
collectionMap(list)
}
fun collectionMap(list:List<Int>) {
// map 會產生新集合
val newList = list.map { it/100 }
for (i in newList) {
println("list2 : $i")
}
}
B. 集合的 filter
函數:同樣 Lambda 會對集合內的每一個元素作用,最後產生一個符合條件的新集合
fun main() {
val list = listOf(100, 200, 300, 400, 500)
collectionMap(list)
collectionFilter(list)
}
fun collectionFilter(list: List<Int>) {
val newList = list.filter { it in 300..400 } // 區間
for (i in newList) {
println("list filter : $i")
}
}
C. 集合的 any
函數:判斷集合內任意的元素是否有符合條件
fun main() {
val list = listOf(100, 200, 300, 400, 500)
collectionMap(list)
collectionFilter(list)
collectionAny(list)
}
fun collectionAny(list: List<Int>) {
val isPass = list.any { it > 200}
println("list any: $isPass")
}
D. 集合的 all
函數,判斷集合內是否每一個元素都符合條件
fun main() {
val list = listOf(100, 200, 300, 400, 500)
collectionMap(list)
collectionFilter(list)
collectionAny(list)
collectionAll(list)
}
fun collectionAll(list: List<Int>) {
val isPass = list.all { it > 200}
println("list all: $isPass")
}
更多的 Kotlin 語言相關文章
在這裡,我們提供了一系列豐富且深入的 Kotlin 語言相關文章,涵蓋了從基礎到進階的各個方面。讓我們一起來探索這些精彩內容!
Kotlin 語言基礎
● Kotlin 語言基礎:想要建立堅實的 Kotlin 基礎?以下這些文章將帶你深入探索 Kotlin 的關鍵基礎和概念,幫你打造更堅固的 Kotlin 語言基礎
Kotlin 特性、特點
● Kotlin 特性、特點:探索 Kotlin 的獨特特性和功能,加深對 Kotlin 語言的理解,並增強對於語言特性的應用
Kotlin 進階:協程、響應式、異步
● Kotlin 進階:協程、響應式、異步:若想深入學習 Kotlin 的進階主題,包括協程應用、Channel 使用、以及 Flow 的探索,請查看以下文章