Kotlin高階函式
張益裕 Michael Chang
- 精誠資訊/恆逸教育訓練中心-資深講師
- 技術分類:程式設計
Java 8開始支援Functional Programming,除了原有的物件導向程式設計語言,也可以使用Lambda expression撰寫程式。不過Java並沒有完整的支援Functional Programming,Lambda expression只能使用在方法或建構式的參數傳遞。
Java 8定義只有一個抽象方法的interface為functional interface,例如下列的宣告:
package java.util.function; import java.util.Objects; @FunctionalInterface public interface Predicate<T> { boolean test(T t); ... }
方法或建構式如果接收functional interface型態的參數,呼叫這個方法的時候就可以使用lambda expression提供參數,例如下列在java.util.stream.Stream中的宣告:
Stream<T> filter(Predicate<? super T> predicate)
Kotlin程式設計語言同時支援物件導向與Functional Programming,Kotlin的function為first-class,這表示Kotlin的function可以儲存為變數,使用在參數的傳遞與回傳給其它higher-order function。
一般的函式可以根據需求定義一個回傳值型態,回傳值可以是任何物件或基本資料型態。高階函式允許函式的回傳值為函式型態,這樣的做法可以讓應用程式的設計更加靈活。為了說明高階函式中的函式回傳型態,下列宣告的類別與列舉型態使用在後續的範例:
// 客戶等級與對應的折扣 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 } // 客戶折扣再減去數量折扣 level.discount - amountDiscount }
設計好相關的函式後就可以靈活的應用:
fun main(args: Array<String>) { // 取得計算折扣的函式變數 val discount = getDiscount(Level.VIP) // 呼叫函式取得訂單的折扣 println( discount( Order(16) ) ) }
為了對高階函式應用能有更進一步認識,下列宣告的類別與列舉型態使用在後續的範例:
// 會員類別,包含名稱與編號 data class Member(val firstName: String, val lastName: String, val id: String) // 會員名稱種類 enum class MemberName {FIRST, LAST, ALL}
應用程式需要提供會員名稱搜尋與過濾的功能,包含first name、last name或全名,為了應付這樣的需求,使用回傳的函式提供應用程式不同功能的需求,是一種最靈活的設計:
// 會員名稱搜尋函式,接收搜尋內容與種類參數 // 回傳會員過濾條件函式,參數為會員物件,回傳值為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) } }