Kotlin 函数

文章目录

函数的定义

函数可以理解成一个小小的加工厂,给入特定的原材料,它就会给出特定的产品。

复制代码
fun [接收者类型.][函数名]([参数名: 参数类型], ...)[:返回值类型] [函数体]

我们只需要写一次,便可以反复使用:

kt 复制代码
fun greeting(name: String) {
    println("Hello $name!")
}


fun main() {
    greeting("Today")
    greeting("Kotlin")
}
复制代码
Hello Today
Hello Kotlin

Note:调用函数时,函数参数有多个,且未指定参数名,需要按参数顺序传入参数。

函数的返回值

函数都会有返回值,默认的返回值是Unit(无论何时,Unit都是唯一的一个值(单例),不可以被实例化),可以省略不写:

kt 复制代码
fun greeting(name: String): Unit {
	println("Hello $name!")
	return Unit
	// return
}

函数的返回值会给到调用处,也就是说下方的getResult()就代表了默认返回值Unit。我们可以直接打印出返回值,或者将其赋值给变量:

kt 复制代码
fun getResult() {}

fun main() {
	val result: Unit = getResult()
	
	println(result)
	print(getResult())
}
复制代码
kotlin.Unit
kotlin.Unit

我们也可以指定返回其他类型:

kt 复制代码
fun plus1(old: Int): Int {
    return old + 1
}


fun main() {
    val new = plus1(0)

    print(new)
}
复制代码
1

甚至可以返回一个函数(这可能有点超纲了,看不懂可以先不理,请参考下方Lambda 表达式匿名函数 ):

这里的getFun返回了一个无参数的、返回值为String类型的函数。

kt 复制代码
fun getFun(): () -> String {
    return { "Hello World" }
}


fun main() {
    val greet = getFun()
    val msg = greet()

    print(msg)
}
复制代码
Hello World

Note:Kotlin 中函数类型的书写格式是(参数名和冒号:可省略):

复制代码
([参数名: 参数类型], ...) -> [返回值类型]

kt 复制代码
fun main() {
	val myFun: (Int) -> Int = {
		it + 1
	}

	print(myFun(0))
}
复制代码
1

与之类似的写法是:

kt 复制代码
fun main() {
	fun myFun(int: Int): Int {
		return int + 1
	}

	print(myFun(0))
}

或者

kotlin 复制代码
fun main() {
	val myFun = fun(int: Int): Int {
		return int + 1
	}

	print(myFun(0))
}

一个函数可以作为另一个函数的参数:

kt 复制代码
fun caller(block: () -> Unit) {
    // 调用 block 函数
    block()
}

fun main() {
    // 调用 caller 函数,传入 lambda
    caller({ print("Hello World") })
}
复制代码
Hello World

如果函数体可以用一句表达式表示,则可以直接用等号=,返回值类型也可以省略:

kt 复制代码
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

参数默认值 & 调用时参数指定

当某个参数为可选时,可以为参数赋默认值(我们一般将可选参数放于必要参数之后,但这不是硬性要求):

kt 复制代码
fun greet(otherParams: Int, name: String = "Kotlin") {
    print("Hello $name")
}


fun main() {
    greet(0)
}
复制代码
Hello Kotlin

我们也可以在调用时指定参数传值(下方的exampleParam):

kt 复制代码
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

函数作用域

在函数中定义的变量、函数,在函数外无法访问:

kt 复制代码
fun main() {
    fun funScope() {
        val name = "Kotlin"
        
        fun innerFun() {}
    }

    // print(name) 无法访问
    // innerFun() 无法访问
}

函数内可以访问函数外定义的变量、函数:

kt 复制代码
fun main() {
    val name = "Kotlin"

    fun outerFun() { print("Outer Fun") }

    fun funScope() {
        // 可以调用外部内容
        println(name)
        outerFun()
    }

    funScope() // 我们需要调用函数才能看到其运行结果
}
复制代码
Kotlin
Outer Fun

Lambda 表达式

我们已经不止一次提到它了,它的写法是这样的:

复制代码
// 匿名函数
{ [参数名: 参数类型], ... ->
	[函数体]
}

不指明类型的函数类型变量,它的类型会是() -> Unit

当 lambda 的参数只有一个时,这个参数可以省略不写,通过it指定:

kt 复制代码
fun main() {
    val myLambda: (String) -> Unit = {
        print("Hello $it")
    }
}

当 lambda 作为函数的最后一个参数进行传递时,可以将花括号{}移到调用的小括号()外面,称为尾部 lambda(trailing lambda):

kt 复制代码
fun caller(name: String, block: () -> Unit) {}

fun main() {
    caller("Kotlin", { print("不移出小括号") })

    caller("Kotlin") {
        print("移出小括号")
    }
}

若没有return,lambda 表达式的最后一个值会作为函数的返回值:

kt 复制代码
fun caller(block: () -> String) {
    print(block())
}

fun main() {
    caller { "ABC" }
}
复制代码
ABC

匿名函数

匿名函数无需写函数名(lambda 表达式也是匿名函数):

复制代码
fun([参数名: 参数类型], ...)[: 返回值类型] [函数体]
kt 复制代码
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 会警告)。用得比较多的场景是函数有参数为函数类型。

kt 复制代码
inline fun caller(block: () -> String) {
    print(block())
}


fun main() {
    caller { "ABC" }
}

printprintln因为参数类型为Any,可能为函数类型。可以看到 JVM 平台它们的实现(actual)其实是 Java 的System.out.printSystem.out.println(可以通过按住Ctrl键,鼠标点击函数,跳转到函数声明处):

kt 复制代码
// 
@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

kt 复制代码
inline fun caller(
    noinline noinlineBlock: () -> Unit
) {}

当内联函数(下方call)可内联的函数类型参数(下方block)被传入的 内联函数(下方noinlineBlock)调用时,需要标记为crossinline

kt 复制代码
inline fun caller(
    noinline noinlineBlock: () -> Unit
) {}


inline fun call(crossinline block: () -> Unit) {
    caller{ block() }
}

扩展函数

还记得我们最开始说的函数类型声明吗?这里有一个接收者

复制代码
fun [接收者类型.][函数名]([参数名: 参数类型], ...)[:返回值类型] [函数体]

我们可以通过声明接收者,将某一函数定义为接收者所拥有的函数,称为其扩展函数。

这可能有点难以理解,因为我们还没有讲到,我在这里做一个简单的解释:

  • 我们定义了一个以Int作为接收者的函数add用于对Int类型数值加上(+)值other
  • 在该函数中,this会指代接收者Int类型,例如这里调用int.add,在addthis + other相当于0 + 3,结果为3,会返回到调用处。
kt 复制代码
fun Int.add(other: Int) = this + other

fun main() {
    val int = 0

    print(int.add(3))
}
复制代码
3

中缀函数

我们可以通过 infix 关键字将一个函数声明为中缀函数,我们还是以上方扩展函数为例(因为中缀函数必须为扩展函数成员方法 ,而且有且仅有一个 参数)。

可以看到运行结果其实是一样的,只是在调用时可以将int.add(3)写成int add 3,函数名作为类似运算符的存在。

kt 复制代码
infix fun Int.add(other: Int) = this + other

fun main() {
    val int = 0

	println(int.add(3))
    print(int add 3)
}
复制代码
3
3

递归函数 & 尾递归函数

递归就是一个函数自己调用自己。

kt 复制代码
fun myFun() {
    myFun()
}

我们细想一下就会发现这样是不可取的,myFun调用了myFun,而被调用的myFun又调了myFun······看来一时半会是停不了了。

如果我们给递归加上条件,当start == end时才递归,它就能够停下来:

kt 复制代码
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

其实就有点像循环:

kt 复制代码
fun main() {
    var start = 0
    val end = 5

    while (start <= end) {
        println("现在是: $start")

        start ++
    }
}

当递归调用在末尾时,可以在fun前加tailrec,使函数成为尾递归函数(tail recursive functions),编译器会优化该递归,生成一个循环(参考示例)。

相关推荐
モンキー・D・小菜鸡儿7 小时前
Android 中 StateFlow 的使用
android·kotlin
我又来搬代码了8 小时前
【Android】【Compose】Compose知识点复习(一)
android·前端·kotlin·android studio
hnlgzb13 小时前
好像kotlin class和kotlin file都可以是activity?
android·开发语言·kotlin
zhangphil13 小时前
Kotlin超时withTimeout超时与ensureActive()取消协程任务执行
kotlin
hnlgzb21 小时前
安卓app开发,如何快速上手kotlin和compose的开发?
android·开发语言·kotlin
alexhilton1 天前
Jetpack Compose 2025年12月版本新增功能
android·kotlin·android jetpack
lin62534221 天前
Android九宫格,1张图到9张图适配;图片自定义UI
android·ui·kotlin
zhangphil2 天前
Kotlin协程buffer缓冲池,调度任务执行
kotlin
モンキー・D・小菜鸡儿2 天前
Android Jetpack Compose 基础控件介绍
android·kotlin·android jetpack·compose
侠***I2 天前
基于OOA-TCN-BiGRU-Attention的鱼鹰算法优化多变量时间序列预测
kotlin