Kotlin進階知識(六)——聲明高階函數

1、函數類型

Kotlin的顯式類型聲明是:java

val sum = { x: Int, y: Int -> x + y }
val action = { println(42) }

// 有兩個Int型參數和Int型返回值的函數
val sum: (Int, Int) -> { x, y -> x + y }
// 沒有參數和返回值的函數
val action(): -> Unit = { println(42) }
複製代碼

聲明函數類型,須要將函數參數類型放在括號中,緊接着是一個箭頭和函數的返回類型,以下:markdown

(Int, String)    ->       Unit
|- 參數類型 -|     |- 返回類型 -|
複製代碼

Unit類型用於表示函數不返回任何有用的值。在聲明一個普通的函數時,Unit類型的返回值是能夠省略的,可是一個函數類型聲明老是須要一個**顯式的返回類型**,因此在這種場景下**Unit不能省略**的。app

函數類型的返回值是可空類型 & 函數自己可空ide

  • 函數類型的返回值是可空類型
var canReturnNull: (Int?, Int?) -> Int? = { x, y ->
    y?.let {
        x?.plus(it)
    }
}

//測試
println(canReturnNull(1, 2))
println(canReturnNull(null, 2))

//輸出結果
3
null
複製代碼
  • 函數自己可空
var funOrNull: ((Int, Int) -> Int) ? = null

//測試
println(funOrNull?.let { it(1, 2) })

//輸出結果
null
複製代碼

**注意:**若是省略了括號,聲明的將會是一個返回值可空的函數類型,而不是一個可空的函數類型的變量.函數

注意該函數自己可空時調用須要使用**類型**測試

2、調用做爲參數的函數

// 定義一個函數類型的參數:operation
fun twoAndThree(operation: (Int, Int) -> Int) {
    // 調用函數類型的參數
    val result = operation(2, 3)
    println("The result is $result")
}

fun twoAndThreeTest() {
    twoAndThree{ a, b -> a + b }
    twoAndThree{ a, b -> a * b }
}

// 輸出結果
The result is 5
The result is 6
複製代碼

調用做爲參數的函數和調用普通函數的語法是同樣的:把括號放在函數名後,並把參數放在括號內ui

函數聲明的示意圖1: 圖1:filter函數的聲明,以一個判斷式做爲參數spa

filter函數以一個判斷式做爲參數。判斷式的類型是一個函數,以字符做爲參數返回boolean類型的值。若是要讓傳遞給判斷式的字符出如今最終返回的字符串中,判斷式須要返回true,反之返回false。code

fun String.filter(predicate: (Char) -> Boolean): String {
    val result = StringBuilder()
    for(index in 0 until length) {
        val element = get(index)
        // 調用做爲參數傳遞給「predicate」的函數
        if(predicate(element)) result.append(element)
    }
    return result.toString()
}

fun stringFilterTest() {
    // 傳遞一個lambda做爲「predicate」參數
    println("abcABC123".filter {
        it in 'a' .. 'z'
    })
}

// 輸出結果
abc
複製代碼

3、在Java中使用函數類

函數類型被聲明爲普通的接口:一個函數類型的變量時FunctionN接口的一個實現。Kotlin標準庫定義了一系列的接口,這些接口對應於不一樣參數樹立的函數:Function0(沒有參數的函數)、Function1<P1, R>(一個參數的函數),等等。每一個接口定義了一個invoke方法,調用這個方法就會執行函數。orm

一個函數類型的變量就是實現了對應的**FunctionN接口的實現類的實例**,實現類的**invoke**方法包含了lambda函數體。

/* kotlin 聲明 */
fun processTheAnswer(f: (Int) -> Int) {
    println(f(42))
}

/* Java */
// 調用processTheAnswer方法
// 在Java代碼中使用函數類型(Java8之前)
    public static void processTheAnswerTest() {
        processTheAnswer(
                new Function1<Integer, Integer>() {
                    @Override
                    public Integer invoke(Integer number) {
                        System.out.println(number);
                        return number + 1;
                    }
                }
        );
    }

// Java 8
processTheAnswer(number -> number + 1)
複製代碼

在Java中能夠很容易地使用Kotlin標準庫中以lambda做爲參數的擴展函數。

可是要注意:必需要顯式地傳遞一個接受者對象做爲第一個參數:

public void printString() {
        List<String> strings = new ArrayList<>();
        strings.add("42");
        // 能夠在Java代碼中使用Kotlin標準庫中的函數
        CollectionsKt.forEach(strings, s -> {
            System.out.println(s);
            // 必需要顯式地返回一個Unit類型的值
            return Unit.INSTANCE;
        });
    }
複製代碼

在**Java中,函數或者lambda能夠返回Unit。但由於在Kotlin中Unit類型是有一個值的,因此須要顯式地返回**它。

注意:一個返回voidlambda不能做爲返回Unit的函數類型的實參! 就像以前的例子中的(String) -> Unit

4、返回函數的函數

enum class Delivery { STANDARD, EXPEDITED }

class Order(val itemCount: Int)

fun getShippingCostCalculator( // 聲明一個返回函數的函數 delivery: Delivery): (Order) -> Double {
    if(delivery == Delivery.EXPEDITED) {
        // 返回lambda
        return { order -> 6 + 2.1 * order.itemCount }
    }

    // 返回lambda
    return { order -> 1.2 * order.itemCount }
}

fun getShippingCostCalculatorTest() {
    // 將返回的函數保存在變量中
    val calculator =
        getShippingCostCalculator(Delivery.EXPEDITED)

    // 調用返回的函數
    println("Shipping costs ${calculator(Order(3))}")
}

// 輸出結果
Shipping costs 12.3
複製代碼

聲明一個返回另外一個函數的函數,須要指定一個函數類型做爲返回類型。

在上述代碼中,getShippingCostCalculator返回了一個函數,這個函數以Order做爲參數並返回一個Double類型的值。

返回一個函數,須要寫一個return表達式,跟上一個lambda一個成員引用,或者其餘的函數類型的表達式,好比一個(函數類型的)局部變量。

相關文章
相關標籤/搜索