1. 程式人生 > >Kotlin學習筆記(九)函式,Lambda表示式

Kotlin學習筆記(九)函式,Lambda表示式

函式

Kotlin中,使用fun關鍵字定義一個函式,函式引數使用 Pascal 表示法定義,即 name: type。引數用逗號隔開。每個引數必須有顯式型別。

fun foo(arg1:String,arg2:Boolean,arg3:Int):Int{
    //do somethong
    return 1
}

當函式只有單個表示式時,可以省略花括號,直接寫在=之後,如果返回值型別可由編譯器推斷出來,也可以省略返回值型別:

fun foo(arg1:String,arg2:Boolean,arg3:Int)=1//省略花括號和返回值型別後的函式形式

具有塊程式碼體的函式,必須顯式指定返回值型別,除非返回值型別是Unit。編譯器不會推斷具有塊程式碼體的函式的返回型別,因為這樣的函式在程式碼體中可能有複雜的控制流,並且返回型別對於讀者(又是甚至是編譯器)是很不明顯的。

fun foo(arg1:String,arg2:Boolean,arg3:Int):Int{
    print("arg1="+arg1+"&arg2="+arg2+"arg3="+arg3)
    return 0
}

呼叫函式使用傳統的方法

var value=foo()

成員函式的呼叫要使用點表示法

R().foo()

函式還可以使用中綴表示法來呼叫,當滿足一下條件時:

infix fun Int.add(x:Int):Int{
    return this+x
}

fun printAdd(){
    val value=2 add 3
//中綴表示法呼叫add函式 print("the value is $value") }

我們還可以在呼叫函式的時候使用命名的引數:

fun foo(arg1:String="Hello Kotlin",arg2:Boolean,arg3:Int){
    print("arg1="+arg1+"&arg2="+arg2+"arg3="+arg3)
}

fun printFoo(){
    foo(arg2 = true,arg3 = 7)//命名引數呼叫foo函式
}

函式引數可以有預設值,當省略相應的引數時使用預設值。與其他語言相比,這可以減少過載數量。

fun foo3(arg1: String,arg2: Boolean=false,arg3: Int=0
)
{//預設引數要放到函式引數的最後面 print("arg1="+arg1+"&arg2="+arg2+"arg3="+arg3) } fun printFoo3(){ foo3("Hello Kotlin")//呼叫時,可以不傳入預設引數的值,這時預設引數就會使用預設值 }

我們還可以為函式定義可變數量的引數,只要用vararg關鍵字修飾即可:

//可變數量的引數
fun foo4(vararg args:Int){
    for (arg in args){
        print(arg.toString()+",")
    }
}

fun printFoo4(){
    foo4(1,2,3,4,5)//1,2,3,4,5,
}

如果我們已經有一個數組並希望將其內容傳給該函式,我們使用伸展(spread)操作符(在陣列前面加 *):

//可變數量的引數
fun foo4(vararg args:Int){
    for (arg in args){
        print(arg.toString()+",")
    }
}

fun printFoo4(){
    val values= intArrayOf(1,2,3,4,5)
    foo4(*values)//使用擴充套件運算子傳一個數組給可變引數
}

高階函式

高階函式可以將一個函式作為引數或返回值:

fun add2(x:Int=0,y:Int=0):Int{
    return x+y
}

fun operate(x:Int=0,y:Int=0,body:(Int,Int)->Int){//body是一個函式型別,傳入兩個Int型別引數,返回一個Int型別引數
    print("this result is "+body(x,y))
}

fun getValue(){
    operate(3,7,::add2)
}

Lambda表示式

以上的operate()方法,我們還有一個更簡潔的呼叫方式,即傳入一個lambda表示式:

fun getValue(){
    operate(3,7,::add2)//函式引數傳入一個函式
    operate(3,7,{x,y->x+y})//函式引數傳入一個lambda表示式
}

Lambda 表示式的完整語法形式,即函式型別的字面值如下:

val sum = { x: Int, y: Int -> x + y }

lambda 表示式總是被大括號括著, 完整語法形式的引數宣告放在括號內,並有可選的型別標註, 函式體跟在一個 -> 符號之後。如果推斷出的該 lambda 的返回型別不是 Unit,那麼該 lambda 主體中的最後一個(或可能是單個)表示式會視為返回值。

如果我們把所有可選標註都留下,看起來如下:

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

當函式引數是最後函式的最後一個引數,並且你傳入一個lambda表示式作為相應的引數,則可以在圓括號之外指定它:

fun getValue(){
    operate(3,7,::add2)//函式引數傳入一個函式
    operate(3,7,{x,y->x+y})//函式引數傳入一個lambda表示式
    operate(3,7){//函式引數作為函式的最後一個引數,並且傳入的是一個lambda表示式,可以在在圓括號外指定
        x,y->x+y
    }
}

如果lambda表示式只有一個引數,kotlin可以自己計算出簽名,它允許我們不宣告唯一的引數,並且隱含的為我們宣告其名稱為it:

fun upCase(str:String,body:(String)->String):String{//body是一個函式引數,傳入一個String型別引數,返回一個String型別
    return body(str)
}
fun transform(){
    upCase("HelloKotlin"){//函式字面值只有一個引數,可以省略引數宣告,其名稱是it
        it.toUpperCase()
    }
}

如果lambda表示式是呼叫的唯一引數,則呼叫中的圓括號可以完全省略:

fun String.upper(body:(String)->String):String{
    return body(this)
}

fun transform(){
    "HelloKotlin".upper { it.toUpperCase() }//lambda是呼叫的唯一引數,則呼叫的圓括號可以省略
}

匿名函式

匿名函式與常規函式一樣,只是省略了函式名稱而已:

fun String.upper(body:(String)->String):String{
    return body(this)
}

fun transform(){
    "HelloKotlin".upper { it.toUpperCase() }//lambda表示式是呼叫的唯一引數,則呼叫的圓括號可以省略
    "HelloKotlin".upper(fun(str:String):String{//將匿名函式作為一個函式引數傳入
        return str.toUpperCase()
    })
}

Lambda表示式和匿名函式之間的另一個區別是非區域性返回的行為。一個不帶標籤的 return 語句總是在用 fun 關鍵字宣告的函式中返回。這意味著 lambda 表示式中的 return 將從包含它的函式返回,而匿名函式中的 return 將從匿名函式自身返回。

fun foo() {
    ints.forEach {
        if (it == 0) return//這個 return 表示式從最直接包圍它的函式即 foo 中返回。
        print(it)
    }
}

值得注意的是:這種非區域性的返回只支援傳給行內函數的 lambda 表示式。 如果我們需要從 lambda 表示式中返回,我們必須給它加標籤並用以限制 return。

fun transform():String{
    "HelloKotlin".upper {
        print(it.toUpperCase())
        return@upper it.toUpperCase()//返回必須加標籤限制
    }
    "HelloKotlin".upper(fun(str:String):String{
        return str.toUpperCase()//從匿名函式返回
    })
}

帶接收者的函式字面值

Kotlin提供了指定的接收者呼叫函式字面值的功能。在函式字面值的函式體中,可以呼叫該接收者物件上的方法而無需任何額外的限定符。
這樣的函式字面值的型別是一個帶有接收者的函式型別:

sum : Int.(other: Int) -> Int

該函式字面值可以這樣呼叫,就像它是接收者物件上的一個方法一樣:

1.sum(2)

匿名函式語法允許你直接指定函式字面值的接收者型別。 如果你需要使用帶接收者的函式型別宣告一個變數,並在之後使用它,這將非常有用。

val sum = fun Int.(other: Int): Int = this + other