kotlin - 函数和闭包
方法的定义
定义语法:
fun [方法名] ( [参数名] : [参数类型] ) : [返回类型]{
... return [返回值]
}
有返回值的函数:
fun multiply(x: Int, y:Int): Int {
return x * y
}
如果函数体中只有一条返回语句我们也可以简写为:
fun multiply(x: Int, y:Int): Int = x * y
也可以写成lamda表达式的形式:
var multiply = { x: Int, y: Int -> x * y }
无返回值的函数:
使用Unit代替返回类型,Unit可以省略
fun log(msg: String): Unit {
print(msg)
}
静态方法:
静态方法是通过companion object和object来实现的
class Test{
companion object{
fun com(){
print("com")
}
}
}
object Test2{
fun to{
print("to")
}
}
这种方式只是在使用起来像java的静态方法,实际上这些方法都是类的成员函数,内部还是使用对象的实例调用的。
顶级方法:
在类外部定义的方法,可理解为静态方法,通过包名.方法名的方式调用:
package a.b.c
fun top(){
print("top")
}
fun main(args: Array<String>) {
a.b.c.top()
}
顶级方法与静态方法区别
通过反编译可以知道,其实他们不在一个类中。
如果有顶级变量会新生成一个[文件名+kt]的类,顶级方法就在其中。
函数的形参
命名参数
命名参数和java中的一致,只是在使用的时候可以指定给那个参数进行赋值。好处是可以不关心传参顺序。
fun main(args: Array<String>) {
log(name = "刘德华", age = 18)
}
fun log(age: Int, name: String) {
print("age:$age,name:$name")
}
注意:一旦使用指定参数名赋值后,这个参数之后的参数都要使用指定参数名赋值。
可选参数
可选参数就是在定义函数的时候给参数赋有默认值,在调用函数时有默认值的参数可以不传。
fun logInfo(
name: String,
age: Int,
married: Boolean = true,
language: String = "Chinese"
) {
println("[name:$name,age:$age,married:$married,language:$language");
}
可变参数
使用vararg关键字来修饰参数和java的可变参数使用一致,可变参数可以在参数的任意位置但只能有一个。
fun getBooks(vararg ids:String){
for(id in ids){
println(id)
}
}
//使用
getBooks("a","b","c")
匿名函数
没有名字的函数叫匿名函数,代码如下:
val func = fun(a:Int,b:Int):Int{
return a+b
}
尾递归函数
如果某个函数的末尾又调用了函数自身且递归调用后没有更多的代码,这种就称为尾递归函数。
注意:尾递归不能再try、 catch、 finally块中使用
尾递归函数需要使用关键字tailrec
来修饰
//定义计算阶乘的函数
fun fact (n : Int) : Int{
if (n == 1) {
return 1
} else {
return n * fact(n - 1)
}
}
改为尾递归的形式如下:
tailrec fun factRec(n: Int, total : Int= 1): Int =
if (n == 1) total else factRec(n - 1 , total * n)
注意:函数的后面只能单纯的调用尾递归函数不能任何表达式
与普通递归函数的区别
编译器会对尾递归进行修改,将其优化成一个快速而高效的基于循环的版本,这样就可以减少可能对内存的消耗。
局部函数
Kotlin 还支持在函数体内部定义函数,这种被放在函数体内定义的函数称为局部函数。
在默认情况下,局部函数对外部是隐藏的,局部函数只能在其封闭( enclosing )函数内有效,其封闭函数也可以返回局部函数,以便程序在其他作用域中使用局部函数。
代码示例如下:
//定义函数,该函数的返回值类型为 Int
fun qetMathFunc(type: String, nn: Int): Int {
//定义一个计算平方的局部函数
fun square(n: Int): Int {
return n * n
}
//定义一个计算立方的用部函数
fun cube(n: Int): Int {
return n * n * n
}
//定义一个计算阶乘的局部函数
fun factorial(n: Int): Int {
var result = 1
for (index in 2..n) {
result *= index
}
return result
}
when (type) {
//调用局部函数
" square" -> return square(nn)
"cube" -> return cube(nn)
else -> return factorial(nn)
}
}
高阶函数
Kotlin 的函数也是一等公民,因此函数本身也具有自己的类型。函数类型就像前面介绍的数据类型一样,既可用于定义变量,也可用作函数的形参类型,还可作为函数的返回值类型。
函数类型的使用
[参数列表]->[返回值类型]
例如如下函数:
fun foo(a:Int,name:String):String{
//...
return "fefefe";
}
fun log(msg:String):Unit{
println(msg)
}
fun Test(){
}
以上函数的类型如下:
(Int,String)->String
String->Unit 也可以写成: (String)
()->Unit 也可以写成:()
函数的引用:
当直接访问这个函数的函数引用,而不是调用函数时,需要在函数名前添加两个冒号代码如下:
fun log(msg:String):Unit{
println(msg)
}
var myfun=::log
使用函数类型作为形参类型
Kotlin 支持像使用其他类型一样使用函数类型,因此完全可以在函数中定义函数类型的形参
fun qetMathFunc(n: Int,func:(Int)->Int): Int {
var a:Int = n+1
return func(a)
}
//定义一个计算平方的局部函数
fun square(n: Int): Int {
return n * n
}
//定义一个计算立方的用部函数
fun cube(n: Int): Int {
return n * n * n
}
//使用
qetMathFunc(10,::square)
从上面的代码可以看出在我们调用用qetMathFunc时,我们可以动态的传入函数,来改变内部的执行逻辑。
使用函数类型作为返回值类型
kotlin 还支持定义函数类型的返回值,这样即可将其他函数作为函数的返回值,代码如下:
//定义函数,该函数的返回值类型为 Int
fun qetMathFunc():(Int)->Int {
//定义一个计算平方的局部函数
fun square(n: Int): Int {
return n * n
}
return::square
}
//使用:
qetMathFunc()(10)
这样可以扩大局部函数的作用域
闭包
闭包就是能够访问其他函数内部的变量的函数。简单的理解就是函数里面定义函数,也叫函数嵌套。
闭包的作用
- 让函数成为编程语言中的一等公民
- 让函数具有对象所有的能力(封装)
- 让函数具有状态
函数有状态其实指:函数执行完成后所声明名的变量不会被释放,仍然在内存中。
代码示例:
fun test(){
var a:Int=3
fun test1(){
print(a)
}
}
kotlin完全支持函数编程,可以在函数的内部声明函数,函数参数可以是函数,函数的返回值可以是函数,具有函数编程语言的所有特点。
闭包函数和普通函数
普通函数不携状态,函数调用完成后里面所有的内容都会释放。下次调用的时候又会重新声明。比如如下代码:
fun test(){
var a=3
println(a);
}
fun main(args: Array<String>) {
test()
}
函数不携带状态,维护起来就需要额外的成本,比如我们需要声明一个额外的变量来记录他的状态,闭包让函数可以携带状态。代码如下:
fun test():()->Unit{
var a=3 //状态
return fun(){
a++
println(a)
}
}
fun main(args: Array<String>) {
var t=test()
t()
t()
t()
}
打印结果为:
4
5
6
可以看出我们test()这个闭包函数的状态并没有丢失,我们也没有声明额外的变量去记录它。