函數與Lambda表達式html
1、函數聲明與調用
2、參數和返回值
3、單表達式函數
4、函數做用域
5、泛型函數
6、尾遞歸函數
7、中綴表示法
8、Lambda表達式的語法
9、高階函數與Lambda表達式
10、匿名函數
11、內聯函數bash
Java 中的方法使用 void 關鍵字聲明:微信
void foo(){}
複製代碼
Kotlin 中的函數使用 fun 關鍵字聲明:閉包
fun foo(){}
複製代碼
用法類似,加入有一個 User 類,裏面有一個 foo() 函數,調用函數的代碼以下: Java代碼ide
new User().foo();
複製代碼
Kotlin代碼函數
User().foo()
複製代碼
聲明有參數的函數,代碼以下: Java代碼post
void foo(String str, int i) {}
複製代碼
Kotlin代碼學習
fun foo(str: String, i: Int) {}
複製代碼
Java先定義類型,後命名;Kotlin先命名,後定義類型,中間用冒號:
分隔。二者都是多個參數中間用逗號,
分隔。 如函數有返回值,代碼以下: Java代碼優化
String foo(String str, int i) {
return "";
}
複製代碼
Kotlin代碼ui
fun foo(str: String, i: Int): String {
return ""
}
複製代碼
Java是把void
替換成返回值的類型,而Kotlin是把返回值聲明在函數的末尾,並用冒號:
分隔。 兩種語言聲明參數和返回值的方式有點類似,而Kotlin還有更強大的功能,例如默認參數
和 命名參數
,以下所示: 函數參數能夠有默認值,當沒有給參數指定值的時候,使用默認值
//給i指定默認值爲1
fun foo(str: String, i: Int = 1) {
println("$str $i")
}
//調用該函數,這個時候能夠只傳一個參數
foo("abc")
//運行代碼,獲得結果爲: abc 1
複製代碼
若是有默認值的參數在無默認值的參數以前,要略過有默認值的參數去給無默認值的參數指定值,要使用命名參數
來指定值,有點繞咱們看代碼:
//有默認值的參數在無默認值的參數以前
fun foo(i: Int = 1, str: String) {
println("$str $i")
}
//foo("hello") //編譯錯誤
foo(str = "hello") //編譯經過,要使用參數的命名來指定值
//運行代碼,獲得結果爲: hello 1
複製代碼
//用 vararg 修飾符標記參數
fun <T> asList(vararg ts: T): List<T> {
val result = ArrayList<T>()
for (t in ts) // ts is an Array
result.add(t)
return result
}
val a = arrayOf(1, 2, 3)
//*a表明把a裏全部元素
val list = asList(-1, 0, *a, 4)
//運行代碼,獲得結果爲: [-1, 0, 1, 2, 3, 4]
複製代碼
在Kotlin中,若是函數的函數體只有一條語句,而且有返回值,那麼能夠省略函數體的大括號,變成單表達式函數。以下所示:
//函數體內只有一條語句,且有返回值
fun foo(): String{
return "abc"
}
//這時能夠省略大括號,變成單表達式函數
fun foo() = "abc"
複製代碼
在 Kotlin 中函數能夠在文件頂層聲明,這意味着你不須要像一些語言如 Java 那樣建立一個類來保存一個函數。此外除了頂層函數,Kotlin 中函數也能夠聲明在局部做用域、做爲成員函數以及擴展函數。
成員函數是指在類或對象裏定義的函數。
Java代碼:
class User {
//在類裏定義函數。
void foo() {}
}
//調用
new User().foo();
複製代碼
Kotlin代碼:
class User() {
//在類裏定義函數。
fun foo() {}
}
//調用
User().foo()
複製代碼
Kotlin支持在函數內嵌套另外一個函數,嵌套在裏面的函數成爲局部函數,以下所示:
fun foo() {
println("outside")
fun inside() {
println("inside")
}
inside()
}
//調用foo()函數
foo()
複製代碼
運行代碼,獲得結果
泛型參數使用尖括號指定,以下所示: 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> {
}
複製代碼
尾遞歸函數是一個遞歸函數,用關鍵字tailrec
來修飾,函數必須將其自身調用做爲它執行的最後一個操做。當一個函數用tailrec
修飾符標記並知足所需的形式時,編譯器會優化該遞歸,留下一個快速而高效的基於循環的版本,無堆棧溢出的風險,舉個例子: 先看一段代碼
fun count(x: Int = 1): Int = if (x == 10) x else count(x - 1)
複製代碼
上面的count()
函數是一個死循環,當咱們調用count()
函數後,會報StackOverflowError。這時能夠用tailrec
修飾符標記該遞歸函數,並將其自身調用做爲它執行的最後一個操做,以下所示:
tailrec fun count(x: Int = 1): Int = if (x == 10) x else count(x - 1)
複製代碼
再次運行代碼,無堆棧溢出。
中綴表示法是調用函數的另外一種方法。若是要使用中綴表示法,須要用infix
關鍵字來修飾函數,且要知足下列條件:
下面來舉個例子:
//擴展函數
infix fun String.removeLetter(str: String): String {
//this指調用者
return this.replace(str, "")
}
//調用
var str = "hello world"
//不使用中綴表示法
println(str.removeLetter("h")) //輸出ello world
//使用中綴表示法
println(str removeLetter "d") //輸出hello worl
//使用中綴表示法調用str removeLetter "d"等同於調用str.removeLetter("d")
//還能夠連續調用
println(str.removeLetter("h").removeLetter("d").removeLetter("l")) // 輸出 eo wor
println(str removeLetter "h" removeLetter "d" removeLetter "l") // 輸出 eo wor
複製代碼
Lambda表達式的語法以下:
舉個例子:
//這是一個Lambda表達式的完整語法形式
val sum = { x: Int, y: Int -> x + y }
//Lambda表達式在大括號中
//參數 x 和 y 在 -> 以前聲明
//參數聲明放在大括號內,並有參數類型標註
//函數體 x + y 在 -> 後面
val i: Int = sum(1, 2)
println(i) //輸出結果爲 3
複製代碼
若是Lambda表達式自動推斷的返回類型不是Unit
,那麼在Lambda表達式函數體中,會把最後一條表達式的值當作是返回值。因此上面的常量sum
的返回值是Int
類型。若是要指定常量sum
的返回值爲Int
類型,能夠這樣寫:
val sum: (Int, Int) -> Int = { x, y -> x + y }
val i: Int = sum(1, 2)
println(i) //輸出結果爲 3
複製代碼
當Lambda表達式只有一個參數的時候,那麼它將能夠省略這個惟一的參數的定義,連同->
也能夠省略。以下所示:
//當Lambda表達式只有一個參數的時候
val getInt: (Int) -> Int = { x -> x + 1 }
val int = getInt(2)
println(int) //輸出結果爲:3
//能夠省略這個參數的定義
//而且將隱含地獎這個參數命名爲 it
val sum: (Int) -> Int = { it + 1 }
val int = sum(2)
println(int) //輸出結果爲:3
複製代碼
上面說到若是Lambda表達式自動推斷的返回類型不是Unit
,那麼在Lambda表達式函數體中,會把最後一條表達式的值當作是返回值。舉個例子:
var sum: (Int) -> Int = {
val i: Int = it + 1
val j: Int = i + 3
val k: Int = it + j - i
i
k
j
}
println(sum(1))
//輸出結果爲 5,也就是 j 的值
複製代碼
高階函數是將函數用做參數或返回值的函數,以下所示:
fun getName(name: String): String {
return name
}
fun printName(a: String, name: (str: String) -> String): String {
var str = "$a${name("Czh")}"
return str
}
//調用
println(printName("Name:", ::getName))
//運行代碼,輸出 Name:Czh
複製代碼
上面代碼中name: (str: String) -> String
是一個函數,擁有函數類型() -> String,接收一個String參數,當咱們執行var str = "$a${name("Czh")}"
這行代碼的時候,至關於執行了var str = "$a${getName("Czh")}"
,並返回了字符串"Czh"。當咱們調用printName("Name:", ::getName)
時,將函數做爲參數傳入高階函數,須要在該函數前加兩個冒號::
做爲標記。
Kotlin提供了Lambda表達式來讓咱們更方便地傳遞函數參數值。Lambda表達式老是被大括號括着;若是有參數的話,其參數在 ->
以前聲明,參數類型能夠省略;若是存在函數體的話,函數體在->
後面,以下所示:
println(printName("Name:", { name -> getName("Czh") }))
//運行代碼,輸出 Name:Czh
複製代碼
若是函數的最後一個參數是一個函數,而且你傳遞一個Lambda表達 式做爲相應的參數,你能夠在圓括號()
以外指定它,以下所示:
println(printName("Name:") { name -> getName("Czh") })
//運行代碼,輸出 Name:Czh
複製代碼
匿名函數與常規函數同樣,只是省略了函數名稱而已。舉個例子
fun(x: Int, y: Int): Int = x + y
複製代碼
匿名函數函數體是表達式,也能夠是代碼段,以下所示:
fun(x: Int, y: Int): Int {
return x + y
}
複製代碼
上面高階函數的例子中的printName
函數的第二個參數也能夠傳入一個匿名函數,以下所示:
println(printName("Name:", fun(str: String): String { return "Czh" }))
//運行代碼,輸出 Name:Czh
複製代碼
使用高階函數會帶來一些運行時的效率損失。每個函數都是一個對象,而且會捕獲一個閉包。 即那些在函數體內會訪問到的變量。 內存分配(對於函數對象和類)和虛擬調用會引入運行時間開銷。這時能夠經過內聯函數消除這類的開銷。舉個例子:
fun printName(a: String, name: (str: String) -> String): String {
var str = "$a${name("Czh")}"
return str
}
println(printName("Name:", { name -> getName("Czh") }))
複製代碼
上面代碼中,printName
函數有一個函數類型的參數,經過Lambda表達式向printName
函數傳入參數值,Kotlin編譯器會爲Lambda表達式單首創建一個對象,再將Lambda表達式轉換爲相應的函數並調用。若是這種狀況出現比較多的時候,就會很消耗資源。這是能夠在函數前使用inline
關鍵字,把Lambda函數內聯到調用處。以下所示:
inline fun printName(a: String, name: (str: String) -> String): String {
var str = "$a${name("Czh")}"
return str
}
println(printName("Name:", { name -> getName("Czh") }))
複製代碼
經過inline
關鍵字,編譯器將Lambda函數內聯到調用處,消除了運行時消耗。但內聯可能致使生成的代碼增長,因此須要避免內聯比較大的Lambda表達式。若是想禁用一些Lambda函數的內聯,可使用noinline
修飾符禁用該Lambda函數的內聯,以下所示:
inline fun printName(name1: (str1: String) -> String
, noinline name2: (str2: String) -> String): String {
var str = "${name1("Name:")}${name2("Czh")}"
return str
}
複製代碼
inline
關鍵字除了可使函數內聯以外,還能內聯沒有幕後字段(field)的屬性,以下所示:
val foo: Foo
inline get() = Foo()
var bar: Bar
get() = ……
inline set(v) { …… }
複製代碼
本篇文章對比了Java方法和Kotlin函數在寫法上的區別,也認識了Lambda函數和還列舉了一些Kotlin函數中比較特別的語法,如中綴表示法等。可見Kotlin中的函數內容仍是不少的,用法也相對複雜,但運用好Kotlin的函數,能使開發變得更簡單。
參考文獻:
Kotlin語言中文站、《Kotlin程序開發入門精要》
推薦閱讀:
從Java到Kotlin(一)爲何使用Kotlin
從Java到Kotlin(二)基本語法
從Java到Kotlin(三)類和接口
從Java到Kotlin(四)對象與泛型
從Java到Kotlin(五)函數與Lambda表達式
從Java到Kotlin(六)擴展與委託
從Java到Kotlin(七)反射和註解
從Java到Kotlin(八)Kotlin的其餘技術
Kotlin學習資料總彙
更多精彩文章請掃描下方二維碼關注微信公衆號"AndroidCzh":這裏將長期爲您分享原創文章、Android開發經驗等! QQ交流羣: 705929135