Kotlin函數式程式設計
文/張益裕 Michael Chang
Kotlin支援高階函式(higher-order function),這表示函式可以宣告為一個變數,所以函式可以宣告接收函式的參數,也可以傳回一個函式。Kotlin同時支援物件導向與函數式技術,使用物件導向的特性,設計複雜與容易擴充的應用程式架構,使用高階函式的特性,可以大幅度簡化程式設計。
在Kotlin程式設計語言,除了各種一般物件型態的變數,您還可以宣告型態為「函式」的變數。跟宣告一般的函式相同,您必須定義函式的參數與回傳值,再使用lambda提供這個函式的實作。
// 接收兩個Int參數並回傳參數合計 val summary01: (Int, Int) -> Int = { x, y -> x + y } // 可以省略型態的宣告 val summary02 = { x: Int, y: Int -> x + y } // 沒有參數、沒有回傳值 val hello01: () -> Unit = { println("Hello Kotlin!") } // 可以省略型態的宣告 val hello02 = { println("Hello Kotlin!") } println("\n===== summary01 & summary02") println( "summary01(3, 5): ${ summary01(3, 5) }" ) println( "summary02(3, 5): ${ summary02(3, 5) }" ) println("\n===== hello01 & hello02") hello01()
在定義Kotlin函式的時候,除了接收一般物件型態參數,也可以接收函式型態參數。根據函式的實作,函式型態參數可以使用lambda expression設定預設值,或是允許接收null參數。
// response參數為函式型態,包含兩個參數,沒有回傳值 fun request(url: String, response: (code: Int, reason: String) -> Unit ){ println("request $url...") val code = 200 val reason = "OK" response(code, reason) } fun main(args: Array<String>) { request("www.codedata.com.tw") { code, reason -> println("$code: $reason") } }
一般的函式可以根據需求定義一個回傳值型態,回傳值可以是任何物件或基本資料型態。高階函式允許函式的回傳值為函式型態,這樣的作法可以讓應用程式的設計更加靈活。
// 客戶等級與對應的折扣 enum class Level(val discount: Double) { NORMAL(0.9), VIP(0.8), GOLDEN(0.7) } // 訂單類別,包含訂單數量 data class Order(var amount: Int) // 計算折扣的函式,接收訂單物件參數 // 回傳計算折扣的函式,參數為訂單物件,回傳值為Double,依照訂單數量計算實際的折扣 fun getDiscount(level: Level): (Order) -> Double = { order -> val amountDiscount = when (order.amount) { in 1..10 -> 0.1 in 11..20 -> 0.2 in 21..30 -> 0.3 else -> 0.4 } // 在lambda expression裡面可以使用外層函式的參數level level.discount - amountDiscount }
使用函式的特性,可以簡化處理複雜折扣計算的應用:
// 取得計算折扣的函式變數 val discount = getDiscount(Level.NORMAL) println( discount( Order(16) ) ) val discount2 = getDiscount(Level.VIP) println( discount( Order(3) ) )
加入回傳函式的特性後,可以比物件導向設計更加靈活,提供另外一個的範例:
// 會員類別,包含名稱與編號 data class Member(val firstName: String, val lastName: String, val id: String) // 會員名稱種類 enum class MemberName {FIRST, LAST, ALL} // 會員名稱搜尋函式,接收搜尋內容與種類參數 // 回傳會員過濾條件函式,參數為會員物件,回傳值為Boolean,依照名稱種類執行判斷 fun getMemberPredicate(prefix: String, pn: MemberName): (Member) -> Boolean = when (pn) { MemberName.FIRST -> { p: Member -> p.firstName.startsWith(prefix) } MemberName.LAST -> { p: Member -> p.lastName.startsWith(prefix) } MemberName.ALL -> { p: Member -> p.firstName.startsWith(prefix) || p.lastName.startsWith(prefix)} } fun main(args: Array<String>) { val people = listOf(Member("Simon", "Johnson", "101"), Member("Mary", "Johnson", "102"), Member("Sam", "Johnson", "101")) println("\n===== First name starts with Si") val predicate01 = getMemberPredicate("Si", MemberName.FIRST) people.filter(predicate01).forEach { println(it) } println("\n===== First name or last name starts with Jo") val predicate02 = getMemberPredicate("Jo", MemberName.ALL) people.filter(predicate02).forEach { println(it) } }
0 意見:
張貼留言