Kotlin 进阶之路(二) 函数
2.1 函数介绍
- 函数的定义
函数也称为方法,用于执行特定的功能。函数一般用于功能代码的封装,在使用时直接调用即可。
函数的语法格式具体如下:
函数声明 函数名称([参数名称: 参数类型, 参数名称: 参数类型] : 返回值类型){ 执行语句 ... return 返回值 }
函数声明: Kotlin 中函数声明使用关键字 fun
函数名称: 每个函数都有函数名称,方便在调用时使用
参数类型: 用于限定调用函数时传入参数的数据类型
参数名称: 是一个变量,用于接收调用函数传入的数据
返回值类型: 用于限定函数返回值的数据类型
返回值: 被 return 语句返回的值,该值会返回给调用者
注意:
当一个函数不返回任何类型的值时,它的返回值类型实际上是 Unit (无类型), 类似 java 中的
void。当返回类型为 Unit 时,可省略不写。
fun doubleValue(x : Int) : Int{ return 2 * x } fun main(args : Array<String>){ var result = doubleValue(3) println("doubleValue() 的返回值为" + result) //doubleValue() 的返回值为6 }
- 函数的类型
Kotlin 中函数可分为 4 种类型,从参数角度分为有参函数和无参函数,从返回值角度可
分为有返回值函数和无返回值函数。
无参无返回值函数
fun argValue(){ println("这是一个无参无返回值的函数") } fun argValue() : Unit{ println("这是一个无参无返回值的函数") }
无参有返回值函数
fun argValue() : String { return "这是一个无参有返回值的函数" }
有参无返回值函数
fun argValue(content : String){ println("该函数传递的参数为" + content) }
有参有返回值函数
fun argValue(content : String) : String{ return content }
- 单表达式函数
如果函数体中只有一行代码,可以把包裹函数体的花括号 { } 替换为等号 = ,把函数
体放在 = 的后面这样的函数称为单标识函数。
fun add(a : Int, b : Int) : Int{ return a + b } //简写1 fun add(a : Int, b : Int) : Int = a + b //简写2 fun add(a : Int, b : Int) = a + b
- 函数的参数
1.具名参数
具名参数是指在调用函数时显示指定形参的名称,这样即使形参和实参的顺序不一致也
没有影响,因为已经明确指定了每个形参对应的实参。具名参数的语法格式如下:
函数名称(形参1=实参1,形参2=实参2,形参3=实参3 …)
形参: 不是实际存在的变量,是在定义函数名和函数体时使用的参数,目的是用于接收调
用该函数传入的参数。在调用函数时,实参将赋值给形参。
实参: 是在调用是传递给函数的参数。实参可以是常量、变量、表达式、函数等。在进行
函数调用时实参必须具有确定的值,以便把值传递给形参。
fun info(name : String, age : Int){ println("姓名: $name") println("年龄: $age") } fun main(args : Array<String>){ info("江小白", 20)//指定形参与实参 info(name = "江小白", age = 20) info(age = 20, name = "江小白") //姓名: 江小白 //年龄: 20 }
2.默认参数
默认参数,是指在定义函数时,可以给函数中每一个形参指定一个默认的实参,语法格式如下
fun 函数名(形参1 : 类型, 形参2 : 类型 = 默认值...){ 函数体 .... }
fun introduce(name : String = "江小白", age : Int){ println("姓名: $name") println("年龄: $age") } fun main(args : Array<String>){ introduce(age = 20)//指定函数中的形参与实参 //姓名: 江小白 //年龄: 20 }
3.可变参数
可变参数是指参数类型确定但个数不确定的参数,通过关键字 vararg 标识,可将其理解为数组。
fun sum(name: String, vararg scores : Int){ var result = 0 scores.forEach { result += it } print("$name 的总成绩: " + result) } fun main(args : Array<String>){ sum("江小白",100, 99, 98, 100, 96) //江小白 的总成绩: 493 }
总结
Kotlin 中的可变参数与 Java 中的可变参数对比
- 可变参数可以出现在参数列表的任意位置
- 可变参数通过关键字 vararg 来修饰
- 可以以数组的形式使用可变参数的形参变量,实参中传递数组时,需要使用 * 前缀操作符
java 中可变参数规则
- 可变参数只能出现在参数列表的最后面
- 用 … 代表可变参数, … 位于变量类型与变量名称之间
- 调用含有可变参数的函数时,编译器为该可变参数隐士创建一个数组,在函数体中以数组的形式访问可变参数
2.2 函数的分类
- 顶层函数
顶层函数又称为包级别函数,可直接放在某一个包中,而不必像 java 一样必须将函数放在某一个类中
在 Kotlin 中,函数可以独立存在。
1.在 ct.kotlin.cp01 包中创建函数
package ct.kotlin.cp01 fun stuInfo(name : String, age : Int){ println("姓名: $name") println("年龄: $age") }
2.在 cx.flutter.cp01 包中创建函数
在 cx.flutter.cp01 中创建一个 Score.kt 文件,在文件中创建一个 sum() 函数和 main() 函
数,其中 sum() 用于计算考试成绩,该函数主要传递三个 Int 类型的参数 math、
chinese、english,在 main() 调用 ct.kotlin.cp01 包中的 stuInfo() 函数以及自身包中的
sum() 函数
package cx.flutter.cp01 import ct.kotlin.cp01.stuInfo fun sum(math : Int, chinese : Int, english : Int){ var result = math + chinese + english println("成绩 $result") } fun main(args: Array<String>){ stuInfo("江小白", 18) sum(100, 9, 100) //姓名: 江小白 //年龄:18 //成绩:209 }
- 成员函数
成员函数是在类或对象内部定义的函数,成员函数的语法格式如下
class 类名{ fun 函数名(){ 执行语句 ... } }
class 是定义类的关键字,当在 main() 函数中调用成员函数时,需要使用 “类的实例.成员函数()”
class Person{ fun hello(){ print("Hello") } } fun main(args : Array<String>){ var person : Person = Person() person.hello() }
- 局部函数
局部函数又称为嵌套函数,主要是在一个函数内部定义另一个函数。语法格式如下
fun 函数名(){ fun 函数名(){ 执行语句 .... } 执行语句 .... }
来看一个例子
fun total(a : Int){ var b : Int = 5 fun add() : Int{ return a + b } println(add()) } fun main(args : Array<String>){ total(3) }
局部函数可以访问外部函数的局部变量,并且在外部函数中可以调用其内部函数的局部函数。
- 递归函数
递归函数是指在函数体内部调用函数本身,来看下通过递归函数求 1~100 的和
fun sum(num : Int) : Int{ if (num == 1){ return 1 }else{ return num + sum(num - 1) } } fun main(args : Array<String>){ println(sum(100)) }
- 尾递归函数
1.尾递归函数的定义
如果一个函数中所有递归调用都出现在函数的末尾,则称这个递归函数是尾递归函数。
尾递归函数的特点就是在递归过程中不用做任何操作,当编译器检测到一个函数调用是
尾递归函数时,就覆盖当前的活动记录而不是在栈中去创建一个新的。这样可使程序运
行效率变得更高,但也容易造成栈溢出问题,在编程中应减少使用尾递归函数。
2.尾递归函数的优化
用修饰符 tailrec 来修饰尾递归函数,避免堆栈溢出,会将尾递归函数转化为 while 循环
- 函数重载
Kotlin 中的函数重载为了避免调用错误,优化了函数命名参数和默认值命名参数。
函数的重载与函数的返回值类型无关,只需同时满足函数名相同,参数个数或参数类型不同即可。
函数重载一般是用在功能相同但参数不同的接口中。来看下下面的重载例子。
//定义一个函数 totalNum() 函数有1个参数,参数类型为 Int fun totalNum(num : Int) : Int{ if (num == 1) return 1 else return num + totalNum(num - 1) } //定义一个函数 totalNum() 函数有1个参数,参数类型为 Float fun totalNum(num : Float) : Float{ if (num == 1F) return 1F else return num + totalNum(num - 1F) } //定义一个函数 totalNum() 函数有2个参数,参数类型为 Int fun totalNum(num : Int, total : Int = 0) : Int{ if (num == 1) return 1 + total else return totalNum(num - 1, num + total) } fun main(args : Array<String>){ var a1 = totalNum(5) var a2 = totalNum(5F) var a3 = totalNum(5, 0) println("a1==" + a1) println("a2==" + a2) println("a2==$a3") //a1==15 //a2==15.0 //a2==15 }