我们在前面已经学习了Kotlin基础,主要介绍各种变量及其使用方法,本节我们将介绍kotlin的函数。
1.创建和使用函数
函数我们一早就见过,程序的入口点在 main。println 也是函数调用,不过这个函数是标准库中已经实现好了的,现在是我们在调用这个函数
fun main() {
println("Hello World")
}
函数就是定义在类中的具有特定功能的一段独立小程序,函数也称为方法。
那我们来看看,如何创建和使用函数。
fun 函数名称([函数参数...]): 返回值类型 {
//函数体
}
函数需要完成我们的任务,在一定条件下可能需要返回结果,再赋值给变量或是参与运算等等,当然如果我们的函数只需要完成任务,不需要返回结果,返回值类型可以不填。
fun hello(): Unit {
//Unit类型表示空,类似于Java中的void,默认情况下省略
println("你好")
}
fun main() {
hello() //调用函数
需要外部传入一些参数的情况,并且需要返回结果的方法。
fun add(a: Int, b: Int): Int {
return a + b
}
//假如只有一行,可以省略return,和前面的if等类似
fun add1(a: Int, b: Double) = a + b //返回类型可自动推断,此处返回double
fun main() {
val sum =add1(1, 2.toDouble())
//toDouble也是函数,函数需要传入double而不是int,需要转换类型
println(sum)
println(add(5, 2))
//带返回值的函数,调用之后得到的返回值,可以直接作为其他函数的参数
}
默认参数、命名参数、可变参数 vararg。
fun greet(name: String = "朋友", punctuation: String = "!") =
//返回一个字符串(自动推断),参数可以提供默认
"你好,$name$punctuation"
fun join(vararg parts: String, sep: String = ", ") =
//vararg是一个关键字,用于声明可变参数, 允许函数接受任意数量的参数
parts.joinToString(sep)
fun main() {
println(greet()) //你好,朋友!
println(greet(punctuation = "~")) //你好,朋友~
println(greet(name = "小林")) //你好,小林!
println(join("A", "B", "C")) //A, B, C
println(join("AAA", 88.toString(), greet(), sep = " | "))
//AAA | 88 | 你好,朋友!
}
默认情况下,变量的获取就是直接返回,设置就是直接修改,不过有些时候我们可能希望修改这些变量获取或修改时执行的操作,我们可以这样。
var str: String = "你好"
set(value) { //只读属性没有
println("懵了吧 $value")
field = value
}
get() {
return "$field,世界"
}
//只读示例
val only: String = "只读"
get() {
println("只读属性")
return field
}
fun main() {
println(str)
str = "haha"
println(str)
println(only)
}
2.递归函数
标准的“递归求和”示例
fun main() {
test(5)
}
//计算0-n的和的功能
fun test(n: Int): Int{
if(n <= 0) return 0 //当n等于0的时候就不再向下,而是直接返回0
return n + test(n - 1) //n不为0就返回当前的n加上test参数n-1的和
}
//test(5)
//→ 5 + test(4)
//→ 4 + test(3)
//→ 3 + test(2)
//→ 2 + test(1)
//→ 1 + test(0)
//→ 0 // 基例:n <= 0
斐波那契数列是一个非常经典的数列,它的定义是:前两个数是1和1,之后的每个数都是前两个数的和。对于求解斐波那契数列第N个数这类问题,我们也可以使用递归来实现:
fun main() {
println(fib(5))
}
fun fib(n: Int): Int{
if(n <= 2) 1 //我们知道前两个一定是1,所以直接返回
return fib(n - 1) + fib(n - 2)
//当前fib(n)的结果就是前两个结果之和,直接递归继续找
}
但是,这个有一个欠缺点,就是使用原始递归:时间复杂度 为O(2^n),指数级增长,太慢了,于是我们使用尾递归:时间复杂度降为了 O(n),空间复杂度也只为 O(1)
fun main() {
// println(fib(5))
// 递归,打印第五个斐波那契数
println(fibTailrec(5))
}
tailrec fun fibTailrec(n: Int, a: Int = 1, b: Int = 1): Int {
return if (n <= 2) b else fibTailrec(n - 1, b, a + b)
//如果只有一行,则可以省略{},全部一行
}
3.使用函数库介绍
Kotlin为我们内置了大量实用的库函数,我们可以使用这些库函数来快速完成某些操作。
幂、绝对值、最大最小、平方根
import kotlin.math.* //导包
fun main() {
2.0.pow(10.0) // 1024.0
abs(-1) // 1
max(19, 20) // 20
min(2, 4) // 2
sqrt(9.0) // 3.0
}
4.高阶函数和lambda表达式
Kotlin中的函数属于一等公民,它支持很多高级特性,甚至可以被存储在变量中,可以作为参数传递给其他高阶函数并从中返回,就想使用普通变量一样。 为了实现这一特性,Kotlin作为一种静态类型的编程语言,使用了一系列函数类型来表示函数,并提供了一套特殊的语言结构,例如lambda表达式。
高阶函数就是“接收函数”或“返回函数”的函数,用来把变化的行为参数化,提升复用与可组合性。
//定义一个高阶函数,接受两个数字类型和一个函数作为参数
fun operate(a: Int, b: Double, f: (Int, Double) -> String): String = f(a, b)
// (Int, Double) -> Int,表示接受两个Int和Double参数并返回String的函数
fun main() {
println(operate(3, 5.0) { x, y -> (x + y).toString() + "哈哈" }) //8
}
**解释:**函数类型用 (参数类型) -> 返回类型 表示,也能起别名让签名更清晰。typealias是 Kotlin中的一个关键字,用于创建类型别名。
//为函数类型创建别名
typealias NumberOperation = (Int, Double) -> String
//使用别名简化函数声明
fun operate(a: Int, b: Double, f: NumberOperation): String = f(a, b)
fun main() {
println(operate(1,45.0) { a, b -> (a+b).toString() +"是答案"})
}
:: 是 Kotlin 中的成员引用操作符,用于引用类的成员函数或属性。
//定义接收函数参数的高阶函数
fun operate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
return operation(a, b)
}
fun main() {
//使用math函数引用作为参数
println(operate(3, 5, Int::plus)) //8
println(operate(3, 5, Int::times)) //15
println(operate(10, 3, Int::minus)) //7
}
上面函数改装。
//这个套娃了点
typealias NumberOperation = (Int, Double) -> String
fun addAndFormat(a: Int, b: Double): String = (a + b).toString() + "是答案"
fun operate(a: Int, b: Double, f: NumberOperation): String = f(a, b)
fun main() {
println(operate(1, 45.0, ::addAndFormat)) //46.0是答案
}
除了引用现成的函数之外,我们也可以使用匿名函数,这是一种没有名称的函数。匿名函数除了没名字之外,其他的用法跟函数是一样的。
fun main() {
val func: (String) -> Int = {
println("这是传入的内容$it") //只有一个参数时候,it代表传入本身
666
//和上节课说的一样,最后一个默认返回值,可以不写return
}
val result = func("哈哈")
println(result)
}
ambda 本质上是“函数字面量”,写法是 {参数列表 -> 函数体}。类型可以显式写,也可依赖推断。
fun main() {
val add: (Int, Int) -> Int = { x, y -> x + y }
//显式函数类型
val add1: (Int, Int) -> Int = { _, y -> y * 2}
//忽略第一个参数,可以使用_代替
val add2 = { x: Int, y: Int -> x + y } //由参数类型推断返回类型
val square: (Int) -> String = { "正方形面积为 " + it * it } //单一参数可用 it
val greet: (String) -> Unit = { name -> println("你好, $name") }
val op = { x: Int, y: Int ->
val sum = x + y
sum * 2 //
//多行lambda的最后一行作为返回值
}
println(add(1, 2)) //3
println(add1(1, 2))
println(add2(1, 2))
println(square(6)) //36
greet("小林")
println(op(1, 2)) //6
}
在Kotlin中,如果函数的最后一个形式参数是一个函数类型,可以直接写在括号后面,就像下面这样。
fun repeatTime(times: Int, action: (Int) -> Unit) {
//传入整数类型,最后一个形参是 (Int) -> Unit,
// 所以调用时可用尾随lambda
for (i in 1..times)
action(i)
}
fun main() {
repeatTime(3) { i ->
println("第 $i 次")
}
}
5.内联函数
内联函数(inline)就是让编译器把函数体直接“展开”到调用处,常用于接收 lambda 的小工具函数,能少分配对象、少一层调用,也能在 lambda 里直接 return 外层函数。
内联函数虽换来性能上的提升,但建议在高阶函数使用,因为普通函数压根没有必要
fun main() {
test { println("打印:$it") }
}
inline fun test(func: (String) -> Unit){
println("内联函数")
func("你好")
}
//编译后
fun main() {
println("内联函数")
val it = "你好"
println("打印:$it")
}
当把一个 lambda 传给内联函数(inline)时,在这个 lambda 里写 return,会直接返回到外层函数,而不是只结束 lambda 自己。这就叫非局部返回。
inline fun check(flag: Boolean, block: () -> Unit) {
println("check: before") //第四步
if (flag) block() //第五步
println("check: after")//不会执行
}
fun demoA() {
println("demoA: start") //第二步
check(true) { //第三步
println("lambda: run") //第六步
return //直接结束 demoA() 第七步
}
println("demoA: end")//不会执行
}
fun main() {
demoA() //第一步
}
//demoA: start
//check: before
//lambda: run
如果你不想跳出外层函数,只想结束 lambda,用标签返回即可:return@函数名。
inline fun check(flag: Boolean, block: () -> Unit) {
println("check: before")
if (flag) block()
println("check: after")
}
fun demoB() {
println("demoB: start")
check(true) {
println("lambda: run")
return@check //只结束 lambda,自身返回
}
println("demoB: end") //会执行
}
fun main() {
demoB()
}
下一节,我们将要讲解类与对象,来看看如何去定义类,使用类。
700

被折叠的 条评论
为什么被折叠?



