柚子快報邀請碼778899分享:Kotlin 函數
柚子快報邀請碼778899分享:Kotlin 函數
文章目錄
函數的定義函數的返回值參數默認值 & 調用時參數指定函數作用域Lambda 表達式匿名函數內聯函數擴展函數中綴函數遞歸函數 & 尾遞歸函數
函數的定義
函數可以理解成一個小小的加工廠,給入特定的原材料,它就會給出特定的產品。
fun [接收者類型.][函數名]([參數名: 參數類型], ...)[:返回值類型] [函數體]
我們只需要寫一次,便可以反復使用:
fun greeting(name: String) {
println("Hello $name!")
}
fun main() {
greeting("Today")
greeting("Kotlin")
}
Hello Today
Hello Kotlin
Note:調用函數時,函數參數有多個,且未指定參數名,需要按參數順序傳入參數。
函數的返回值
函數都會有返回值,默認的返回值是Unit(無論何時,Unit都是唯一的一個值(單例),不可以被實例化),可以省略不寫:
fun greeting(name: String): Unit {
println("Hello $name!")
return Unit
// return
}
函數的返回值會給到調用處,也就是說下方的getResult()就代表了默認返回值Unit。我們可以直接打印出返回值,或者將其賦值給變量:
fun getResult() {}
fun main() {
val result: Unit = getResult()
println(result)
print(getResult())
}
kotlin.Unit
kotlin.Unit
我們也可以指定返回其他類型:
fun plus1(old: Int): Int {
return old + 1
}
fun main() {
val new = plus1(0)
print(new)
}
1
甚至可以返回一個函數(這可能有點超綱了,看不懂可以先不理,請參考下方Lambda 表達式和匿名函數): 這里的getFun返回了一個無參數的、返回值為String類型的函數。
fun getFun(): () -> String {
return { "Hello World" }
}
fun main() {
val greet = getFun()
val msg = greet()
print(msg)
}
Hello World
Note:Kotlin 中函數類型的書寫格式是(參數名和冒號:可省略):
([參數名: 參數類型], ...) -> [返回值類型]
如
fun main() {
val myFun: (Int) -> Int = {
it + 1
}
print(myFun(0))
}
1
與之類似的寫法是:
fun main() {
fun myFun(int: Int): Int {
return int + 1
}
print(myFun(0))
}
或者
fun main() {
val myFun = fun(int: Int): Int {
return int + 1
}
print(myFun(0))
}
一個函數可以作為另一個函數的參數:
fun caller(block: () -> Unit) {
// 調用 block 函數
block()
}
fun main() {
// 調用 caller 函數,傳入 lambda
caller({ print("Hello World") })
}
Hello World
如果函數體可以用一句表達式表示,則可以直接用等號=,返回值類型也可以省略:
fun getYear() = 2024
fun getInt(boolean: Boolean) = if (boolean) 1 else 0
fun getBoolean(int: Int) = when(int) {
0 -> false
else -> true
}
fun main() {
println(getYear())
println(getInt(true))
print(getBoolean(1))
}
2024
1
true
參數默認值 & 調用時參數指定
當某個參數為可選時,可以為參數賦默認值(我們一般將可選參數放于必要參數之后,但這不是硬性要求):
fun greet(otherParams: Int, name: String = "Kotlin") {
print("Hello $name")
}
fun main() {
greet(0)
}
Hello Kotlin
我們也可以在調用時指定參數傳值(下方的exampleParam):
fun greet(name: String, exampleParam: Int = 0, exampleParam1: Int = 1) {
println("Hello $name")
print("exampleParam: $exampleParam, exampleParam1: $exampleParam1")
}
fun main() {
greet("Kotlin", exampleParam = 2)
}
Hello Kotlin
exampleParam: 2, exampleParam1: 1
函數作用域
在函數中定義的變量、函數,在函數外無法訪問:
fun main() {
fun funScope() {
val name = "Kotlin"
fun innerFun() {}
}
// print(name) 無法訪問
// innerFun() 無法訪問
}
函數內可以訪問函數外定義的變量、函數:
fun main() {
val name = "Kotlin"
fun outerFun() { print("Outer Fun") }
fun funScope() {
// 可以調用外部內容
println(name)
outerFun()
}
funScope() // 我們需要調用函數才能看到其運行結果
}
Kotlin
Outer Fun
Lambda 表達式
我們已經不止一次提到它了,它的寫法是這樣的:
// 匿名函數
{ [參數名: 參數類型], ... ->
[函數體]
}
不指明類型的函數類型變量,它的類型會是() -> Unit。 當 lambda 的參數只有一個時,這個參數可以省略不寫,通過it指定:
fun main() {
val myLambda: (String) -> Unit = {
print("Hello $it")
}
}
當 lambda 作為函數的最后一個參數進行傳遞時,可以將花括號{}移到調用的小括號()外面,稱為尾部 lambda(trailing lambda):
fun caller(name: String, block: () -> Unit) {}
fun main() {
caller("Kotlin", { print("不移出小括號") })
caller("Kotlin") {
print("移出小括號")
}
}
若沒有return,lambda 表達式的最后一個值會作為函數的返回值:
fun caller(block: () -> String) {
print(block())
}
fun main() {
caller { "ABC" }
}
ABC
匿名函數
匿名函數無需寫函數名(lambda 表達式也是匿名函數):
fun([參數名: 參數類型], ...)[: 返回值類型] [函數體]
fun main() {
val mySingleLineFun = fun() = 1
val myFun = fun(name: String) {
print("Hello $name")
}
println(mySingleLineFun())
myFun("Kotlin")
}
1
Hello Kotlin
內聯函數
內聯函數可以將參數中 lambda 表達式的代碼插入到函數調用處,提高性能。聲明內聯函數只需要在fun前加inline。內聯函數會使編譯后的代碼更加龐大,我們必須在最合適的時候使用它(錯誤使用時 IDEA 會警告)。用得比較多的場景是函數有參數為函數類型。
inline fun caller(block: () -> String) {
print(block())
}
fun main() {
caller { "ABC" }
}
print或println因為參數類型為Any,可能為函數類型??梢钥吹?JVM 平臺它們的實現(actual)其實是 Java 的System.out.print或System.out.println(可以通過按住Ctrl鍵,鼠標點擊函數,跳轉到函數聲明處):
//
@kotlin.internal.InlineOnly
public actual inline fun print(message: Any?) {
System.out.print(message)
}
...
@kotlin.internal.InlineOnly
public actual inline fun println(message: Any?) {
System.out.println(message)
}
如果不希望某一函數類型的參數被內聯時,可以將其標記為noinline:
inline fun caller(
noinline noinlineBlock: () -> Unit
) {}
當內聯函數(下方call)可內聯的函數類型參數(下方block)被傳入的非內聯函數(下方noinlineBlock)調用時,需要標記為crossinline:
inline fun caller(
noinline noinlineBlock: () -> Unit
) {}
inline fun call(crossinline block: () -> Unit) {
caller{ block() }
}
擴展函數
還記得我們最開始說的函數類型聲明嗎?這里有一個接收者:
fun [接收者類型.][函數名]([參數名: 參數類型], ...)[:返回值類型] [函數體]
我們可以通過聲明接收者,將某一函數定義為接收者所擁有的函數,稱為其擴展函數。 這可能有點難以理解,因為我們還沒有講到類,我在這里做一個簡單的解釋:
我們定義了一個以Int作為接收者的函數add用于對Int類型數值加上(+)值other。在該函數中,this會指代接收者Int類型,例如這里調用int.add,在add中this + other相當于0 + 3,結果為3,會返回到調用處。
fun Int.add(other: Int) = this + other
fun main() {
val int = 0
print(int.add(3))
}
3
中綴函數
我們可以通過 infix 關鍵字將一個函數聲明為中綴函數,我們還是以上方擴展函數為例(因為中綴函數必須為擴展函數或成員方法,而且有且僅有一個參數)。 可以看到運行結果其實是一樣的,只是在調用時可以將int.add(3)寫成int add 3,函數名作為類似運算符的存在。
infix fun Int.add(other: Int) = this + other
fun main() {
val int = 0
println(int.add(3))
print(int add 3)
}
3
3
遞歸函數 & 尾遞歸函數
遞歸就是一個函數自己調用自己。
fun myFun() {
myFun()
}
我們細想一下就會發(fā)現這樣是不可取的,myFun調用了myFun,而被調用的myFun又調了myFun······看來一時半會是停不了了。 如果我們給遞歸加上條件,當start == end時才遞歸,它就能夠停下來:
fun countTo(end: Int, start: Int = 0) {
println("現在是: $start")
if (start == end) return
else countTo(end, start + 1)
}
fun main() {
countTo(5)
}
現在是: 0
現在是: 1
現在是: 2
現在是: 3
現在是: 4
現在是: 5
其實就有點像循環(huán):
fun main() {
var start = 0
val end = 5
while (start <= end) {
println("現在是: $start")
start ++
}
}
當遞歸調用在末尾時,可以在fun前加tailrec,使函數成為尾遞歸函數(tail recursive functions?),編譯器會優(yōu)化該遞歸,生成一個循環(huán)(參考示例)。
柚子快報邀請碼778899分享:Kotlin 函數
推薦文章
本文內容根據網絡資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。
轉載請注明,如有侵權,聯系刪除。