学习Kotlin(五)函数与Lambda表达式
?
推薦閱讀:
學(xué)習(xí)Kotlin(一)為什么使用Kotlin
學(xué)習(xí)Kotlin(二)基本語(yǔ)法
學(xué)習(xí)Kotlin(三)類和接口
學(xué)習(xí)Kotlin(四)對(duì)象與泛型
學(xué)習(xí)Kotlin(五)函數(shù)與Lambda表達(dá)式
學(xué)習(xí)Kotlin(六)擴(kuò)展與委托
學(xué)習(xí)Kotlin(七)反射和注解
學(xué)習(xí)Kotlin(八)其他技術(shù)
Kotlin學(xué)習(xí)資料總匯
?
目錄
一、函數(shù)聲明與調(diào)用
二、參數(shù)和返回值
三、單表達(dá)式函數(shù)
四、函數(shù)作用域
五、泛型函數(shù)
六、尾遞歸函數(shù)
七、中綴表示法
八、Lambda表達(dá)式的語(yǔ)法
九、高階函數(shù)與Lambda表達(dá)式
十、匿名函數(shù)
十一、內(nèi)聯(lián)函數(shù)
一、函數(shù)聲明與調(diào)用
Java 中的方法使用 void 關(guān)鍵字聲明:
void foo(){}Kotlin 中的函數(shù)使用 fun 關(guān)鍵字聲明:
fun foo(){}用法相似,加入有一個(gè) User 類,里面有一個(gè) foo() 函數(shù),調(diào)用函數(shù)的代碼如下:Java代碼
new User().foo();Kotlin代碼
User().foo()二、參數(shù)和返回值
聲明有參數(shù)的函數(shù),代碼如下:Java代碼
void foo(String str, int i) {}Kotlin代碼
fun foo(str: String, i: Int) {}Java先定義類型,后命名;Kotlin先命名,后定義類型,中間用冒號(hào):分隔。兩者都是多個(gè)參數(shù)中間用逗號(hào),分隔。如函數(shù)有返回值,代碼如下:Java代碼
String foo(String str, int i) {return ""; }Kotlin代碼
fun foo(str: String, i: Int): String {return "" }Java是把void替換成返回值的類型,而Kotlin是把返回值聲明在函數(shù)的末尾,并用冒號(hào):分隔。兩種語(yǔ)言聲明參數(shù)和返回值的方式有點(diǎn)相似,而Kotlin還有更強(qiáng)大的功能,例如默認(rèn)參數(shù)和 命名參數(shù),如下所示:函數(shù)參數(shù)可以有默認(rèn)值,當(dāng)沒(méi)有給參數(shù)指定值的時(shí)候,使用默認(rèn)值
//給i指定默認(rèn)值為1 fun foo(str: String, i: Int = 1) {println("$str $i") } //調(diào)用該函數(shù),這個(gè)時(shí)候可以只傳一個(gè)參數(shù) foo("abc") //運(yùn)行代碼,得到結(jié)果為: abc 1如果有默認(rèn)值的參數(shù)在無(wú)默認(rèn)值的參數(shù)之前,要略過(guò)有默認(rèn)值的參數(shù)去給無(wú)默認(rèn)值的參數(shù)指定值,要使用命名參數(shù)來(lái)指定值,有點(diǎn)繞我們看代碼:
//有默認(rèn)值的參數(shù)在無(wú)默認(rèn)值的參數(shù)之前 fun foo(i: Int = 1, str: String) {println("$str $i") } //foo("hello") //編譯錯(cuò)誤 foo(str = "hello") //編譯通過(guò),要使用參數(shù)的命名來(lái)指定值 //運(yùn)行代碼,得到結(jié)果為: hello 1- 可變數(shù)量的參數(shù)
函數(shù)的參數(shù)可以用 vararg 修飾符標(biāo)記,表示允許將可變數(shù)量的參數(shù)傳遞給函數(shù),如下所示:
三、單表達(dá)式函數(shù)
在Kotlin中,如果函數(shù)的函數(shù)體只有一條語(yǔ)句,并且有返回值,那么可以省略函數(shù)體的大括號(hào),變成單表達(dá)式函數(shù)。如下所示:
//函數(shù)體內(nèi)只有一條語(yǔ)句,且有返回值 fun foo(): String{return "abc" } //這時(shí)可以省略大括號(hào),變成單表達(dá)式函數(shù) fun foo() = "abc"四、函數(shù)作用域
在 Kotlin 中函數(shù)可以在文件頂層聲明,這意味著你不需要像一些語(yǔ)言如 Java 那樣創(chuàng)建一個(gè)類來(lái)保存一個(gè)函數(shù)。此外除了頂層函數(shù),Kotlin 中函數(shù)也可以聲明在局部作用域、作為成員函數(shù)以及擴(kuò)展函數(shù)。
1. 成員函數(shù)
成員函數(shù)是指在類或?qū)ο罄锒x的函數(shù)。
Java代碼:
class User {//在類里定義函數(shù)。void foo() {} } //調(diào)用 new User().foo();Kotlin代碼:
class User() {//在類里定義函數(shù)。fun foo() {} } //調(diào)用 User().foo()2. 局部函數(shù)
Kotlin支持在函數(shù)內(nèi)嵌套另一個(gè)函數(shù),嵌套在里面的函數(shù)成為局部函數(shù),如下所示:
fun foo() {println("outside")fun inside() {println("inside")}inside() }//調(diào)用foo()函數(shù) foo()運(yùn)行代碼,得到結(jié)果
而Java中沒(méi)有局部函數(shù)這一概念。
?
五、泛型函數(shù)
泛型參數(shù)使用尖括號(hào)指定,如下所示:Java代碼
<T> void print(T t) { }<T> List<T> printList(T t) { }Kotlin代碼
fun <T> printList(item: T) { }fun <T> printList(item: T): List<T> { }六、尾遞歸函數(shù)
尾遞歸函數(shù)是一個(gè)遞歸函數(shù),用關(guān)鍵字tailrec來(lái)修飾,函數(shù)必須將其自身調(diào)用作為它執(zhí)行的最后一個(gè)操作。當(dāng)一個(gè)函數(shù)用tailrec修飾符標(biāo)記并滿足所需的形式時(shí),編譯器會(huì)優(yōu)化該遞歸,留下一個(gè)快速而高效的基于循環(huán)的版本,無(wú)堆棧溢出的風(fēng)險(xiǎn),舉個(gè)例子:先看一段代碼
fun count(x: Int = 1): Int = if (x == 10) x else count(x - 1)上面的count()函數(shù)是一個(gè)死循環(huán),當(dāng)我們調(diào)用count()函數(shù)后,會(huì)報(bào)StackOverflowError。這時(shí)可以用tailrec修飾符標(biāo)記該遞歸函數(shù),并將其自身調(diào)用作為它執(zhí)行的最后一個(gè)操作,如下所示:
tailrec fun count(x: Int = 1): Int = if (x == 10) x else count(x - 1)再次運(yùn)行代碼,無(wú)堆棧溢出。
七、中綴表示法
中綴表示法是調(diào)用函數(shù)的另一種方法。如果要使用中綴表示法,需要用infix 關(guān)鍵字來(lái)修飾函數(shù),且要滿足下列條件:
- 它們必須是成員函數(shù)或擴(kuò)展函數(shù);
- 它們必須只有一個(gè)參數(shù);
- 其參數(shù)不得接受可變數(shù)量的參數(shù)。
下面來(lái)舉個(gè)例子:
//擴(kuò)展函數(shù) infix fun String.removeLetter(str: String): String {//this指調(diào)用者return this.replace(str, "") }//調(diào)用 var str = "hello world" //不使用中綴表示法 println(str.removeLetter("h")) //輸出ello world //使用中綴表示法 println(str removeLetter "d") //輸出hello worl //使用中綴表示法調(diào)用str removeLetter "d"等同于調(diào)用str.removeLetter("d")//還可以連續(xù)調(diào)用 println(str.removeLetter("h").removeLetter("d").removeLetter("l")) // 輸出 eo wor println(str removeLetter "h" removeLetter "d" removeLetter "l") // 輸出 eo wor八、Lambda表達(dá)式的語(yǔ)法
Lambda表達(dá)式的語(yǔ)法如下:
- Lambda 表達(dá)式總是括在大括號(hào)中;
- 其參數(shù)(如果有的話)在 -> 之前聲明(參數(shù)類型可以省略);
- 函數(shù)體(如果存在的話)在 -> 后面。
舉個(gè)例子:
//這是一個(gè)Lambda表達(dá)式的完整語(yǔ)法形式 val sum = { x: Int, y: Int -> x + y } //Lambda表達(dá)式在大括號(hào)中 //參數(shù) x 和 y 在 -> 之前聲明 //參數(shù)聲明放在大括號(hào)內(nèi),并有參數(shù)類型標(biāo)注 //函數(shù)體 x + y 在 -> 后面val i: Int = sum(1, 2) println(i) //輸出結(jié)果為 3如果Lambda表達(dá)式自動(dòng)推斷的返回類型不是Unit,那么在Lambda表達(dá)式函數(shù)體中,會(huì)把最后一條表達(dá)式的值當(dāng)做是返回值。所以上面的常量sum 的返回值是Int類型。如果要指定常量sum的返回值為Int類型,可以這樣寫(xiě):
val sum: (Int, Int) -> Int = { x, y -> x + y }val i: Int = sum(1, 2) println(i) //輸出結(jié)果為 3當(dāng)Lambda表達(dá)式只有一個(gè)參數(shù)的時(shí)候,那么它將可以省略這個(gè)唯一的參數(shù)的定義,連同->也可以省略。如下所示:
//當(dāng)Lambda表達(dá)式只有一個(gè)參數(shù)的時(shí)候 val getInt: (Int) -> Int = { x -> x + 1 } val int = getInt(2) println(int) //輸出結(jié)果為:3//可以省略這個(gè)參數(shù)的定義 //并且將隱含地獎(jiǎng)這個(gè)參數(shù)命名為 it val sum: (Int) -> Int = { it + 1 } val int = sum(2) println(int) //輸出結(jié)果為:3上面說(shuō)到如果Lambda表達(dá)式自動(dòng)推斷的返回類型不是Unit,那么在Lambda表達(dá)式函數(shù)體中,會(huì)把最后一條表達(dá)式的值當(dāng)做是返回值。舉個(gè)例子:
var sum: (Int) -> Int = {val i: Int = it + 1val j: Int = i + 3val k: Int = it + j - iikj } println(sum(1)) //輸出結(jié)果為 5,也就是 j 的值九、高階函數(shù)與Lambda表達(dá)式
高階函數(shù)是將函數(shù)用作參數(shù)或返回值的函數(shù),如下所示:
fun getName(name: String): String {return name }fun printName(a: String, name: (str: String) -> String): String {var str = "$a${name("Czh")}"return str }//調(diào)用 println(printName("Name:", ::getName)) //運(yùn)行代碼,輸出 Name:Czh上面代碼中name: (str: String) -> String是一個(gè)函數(shù),擁有函數(shù)類型() -> String,接收一個(gè)String參數(shù),當(dāng)我們執(zhí)行var str = "$a${name("Czh")}"這行代碼的時(shí)候,相當(dāng)于執(zhí)行了var str = "$a${getName("Czh")}",并返回了字符串"Czh"。當(dāng)我們調(diào)用printName("Name:", ::getName)時(shí),將函數(shù)作為參數(shù)傳入高階函數(shù),需要在該函數(shù)前加兩個(gè)冒號(hào)::作為標(biāo)記。
Kotlin提供了Lambda表達(dá)式來(lái)讓我們更方便地傳遞函數(shù)參數(shù)值。Lambda表達(dá)式總是被大括號(hào)括著;如果有參數(shù)的話,其參數(shù)在 -> 之前聲明,參數(shù)類型可以省略;如果存在函數(shù)體的話,函數(shù)體在-> 后面,如下所示:
println(printName("Name:", { name -> getName("Czh") })) //運(yùn)行代碼,輸出 Name:Czh如果函數(shù)的最后一個(gè)參數(shù)是一個(gè)函數(shù),并且你傳遞一個(gè)Lambda表達(dá)式作為相應(yīng)的參數(shù),你可以在圓括號(hào)()之外指定它,如下所示:
println(printName("Name:") { name -> getName("Czh") }) //運(yùn)行代碼,輸出 Name:Czh十、匿名函數(shù)
匿名函數(shù)與常規(guī)函數(shù)一樣,只是省略了函數(shù)名稱而已。舉個(gè)例子
fun(x: Int, y: Int): Int = x + y匿名函數(shù)函數(shù)體是表達(dá)式,也可以是代碼段,如下所示:
fun(x: Int, y: Int): Int {return x + y }上面高階函數(shù)的例子中的printName函數(shù)的第二個(gè)參數(shù)也可以傳入一個(gè)匿名函數(shù),如下所示:
println(printName("Name:", fun(str: String): String { return "Czh" })) //運(yùn)行代碼,輸出 Name:Czh十一、內(nèi)聯(lián)函數(shù)
1.內(nèi)聯(lián)函數(shù)
使用高階函數(shù)會(huì)帶來(lái)一些運(yùn)行時(shí)的效率損失。每一個(gè)函數(shù)都是一個(gè)對(duì)象,并且會(huì)捕獲一個(gè)閉包。 即那些在函數(shù)體內(nèi)會(huì)訪問(wèn)到的變量。 內(nèi)存分配(對(duì)于函數(shù)對(duì)象和類)和虛擬調(diào)用會(huì)引入運(yùn)行時(shí)間開(kāi)銷。這時(shí)可以通過(guò)內(nèi)聯(lián)函數(shù)消除這類的開(kāi)銷。舉個(gè)例子:
fun printName(a: String, name: (str: String) -> String): String {var str = "$a${name("Czh")}"return str }println(printName("Name:", { name -> getName("Czh") }))上面代碼中,printName函數(shù)有一個(gè)函數(shù)類型的參數(shù),通過(guò)Lambda表達(dá)式向printName函數(shù)傳入?yún)?shù)值,Kotlin編譯器會(huì)為L(zhǎng)ambda表達(dá)式單獨(dú)創(chuàng)建一個(gè)對(duì)象,再將Lambda表達(dá)式轉(zhuǎn)換為相應(yīng)的函數(shù)并調(diào)用。如果這種情況出現(xiàn)比較多的時(shí)候,就會(huì)很消耗資源。這是可以在函數(shù)前使用inline關(guān)鍵字,把Lambda函數(shù)內(nèi)聯(lián)到調(diào)用處。如下所示:
inline fun printName(a: String, name: (str: String) -> String): String {var str = "$a${name("Czh")}"return str }println(printName("Name:", { name -> getName("Czh") }))2.禁用內(nèi)聯(lián)
通過(guò)inline關(guān)鍵字,編譯器將Lambda函數(shù)內(nèi)聯(lián)到調(diào)用處,消除了運(yùn)行時(shí)消耗。但內(nèi)聯(lián)可能導(dǎo)致生成的代碼增加,所以需要避免內(nèi)聯(lián)比較大的Lambda表達(dá)式。如果想禁用一些Lambda函數(shù)的內(nèi)聯(lián),可以使用noinline修飾符禁用該Lambda函數(shù)的內(nèi)聯(lián),如下所示:
inline fun printName(name1: (str1: String) -> String, noinline name2: (str2: String) -> String): String {var str = "${name1("Name:")}${name2("Czh")}"return str }3.內(nèi)聯(lián)屬性
inline關(guān)鍵字除了可以使函數(shù)內(nèi)聯(lián)之外,還能內(nèi)聯(lián)沒(méi)有幕后字段(field)的屬性,如下所示:
val foo: Fooinline get() = Foo()var bar: Barget() = ……inline set(v) { …… }4.內(nèi)聯(lián)的inline ,noinline和crossinline作用和反編譯解析
https://mp.weixin.qq.com/s/HD_hqi8QCb6co7IaIh6KMg
總結(jié)
本篇文章對(duì)比了Java方法和Kotlin函數(shù)在寫(xiě)法上的區(qū)別,也認(rèn)識(shí)了Lambda函數(shù)和還列舉了一些Kotlin函數(shù)中比較特別的語(yǔ)法,如中綴表示法等??梢?jiàn)Kotlin中的函數(shù)內(nèi)容還是很多的,用法也相對(duì)復(fù)雜,但運(yùn)用好Kotlin的函數(shù),能使開(kāi)發(fā)變得更簡(jiǎn)單。
原文鏈接:https://juejin.im/post/5a94fa66f265da4e95190e44
總結(jié)
以上是生活随笔為你收集整理的学习Kotlin(五)函数与Lambda表达式的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 学习Kotlin(四)对象与泛型
- 下一篇: 学习Kotlin(六)扩展与委托