普通函数声明

使用 func 关键字声明一个函数,像这样

1
2
3
fun add(a:Int,b:Int):Int{
return a+b
}

kotlin中,所有函数的参数都是val的,即不可变参数
如果函数体只有一行代码,可以简洁点:

1
fun add(a: Int, b: Int): Int = a + b

更简单点,返回值自动推断:

1
fun add(a: Int, b: Int) = a + b

###带默认值的函数
可以在声明函数参数的时候,直接指定默认值,如果调用时没有传入,将使用默认值,带有默认值的参数可以在任何位置

1
fun add(a: Int=1, b: Int) = a + b

调用的时候,可以使用参数名=值的形式给出参数

1
add(b=1)

换个位置声明默认值:

1
2
3
4
5
6
fun add(a: Int , b: Int=1) = a + b
fun adds (a:Int,b:Int=1,c:Int)= a+b+c
fun main(vararg args:String){
add(1) //自动匹配第一个参数
adds (1,c=2) //默认值之后的参数需要显示指出参数名
}

函数匹配优先级:

1
2
3
4
5
fun add(a:Int) = a*a
fun add(a: Int , b: Int=1) = a + b
fun main(vararg args:String){
println(add(3)) //输出 9
}

当有多个函数匹配时,带默认值参数个数少的优先级越高,不带默认值的优先级最高

可变参数

kotlin中,使用 vararg 关键字来标识可变参数
java一样,多个参数会被封装成数组赋值给a

1
2
3
4
5
6
7
fun add(vararg a: Int, b: Int):Int{
var sum :Int = 0
a.forEach {
sum+=it
}
return sum+b
}

java不同的是,在java中我们可以直接将一个数组赋值给可变参数,这有时候会引起混淆,因此在kotlin中,我们需要显示使用运算符*将数组解构成元素
比如我们可以这样调用上面的方法:

1
2
3
4
fun main(vararg args: String) {
var arr = IntArray(10) { it }
add(*arr, b = 1)
}

注意,第二个参数我们需要明确指出他的参数名,否则他会被当作可变参数中的一个值

使用lambda

lambda是一种特殊的函数。和传统函数不同的是,他可以被存储,传递,并且可以捕获外部变量形成闭包。

#####lambda的类型
因为lambda本质上还是对象,因此他是有类型的。
lambda的类型格式为:

1
(参数列表)->返回值类型

lambdabody结构为:

1
2
3
4
5
{形参列表->
语句
...
最后一个语句的值为返回值(如果需要返回值)
}

比如:

1
val max:(Int,Int)->Int = {a,b -> if (a>b) a else b}

max用于比较两个整数,并返回他们中的最大者。因为这边接收了两个参数,因此我们需要在lambdabody中显示的为这两个参数指定一个名字,就像我们需要为函数指定形参名。
上面的例子也可以这样写:

1
val max =  { a:Int,b:Int-> if (a>b) a else b}  as (Int,Int)->Unit

不过这是比较2b的写法了,使用aslambda进行类型转换,然后max的类型自动推出,这里我只是想说明lambda本质上还是个对象,因此一样可以进行类型转换。

高阶函数

lambda可以作为函数的参数或者返回值,举个例子:

1
2
3
4
5
6
7
8
9
10
11
12
fun <T> forEach(list:List<T>,block:(t:T)->Unit){
for (t in list){
block(t)
}
}

fun main(vararg args:String){
var list = listOf(1,2,3,4,5)
forEach(list){
println(it)
}
}

在上面的例子中,我们声明了一个forEach的函数,他的功能是遍历一个列表,并对列表中的每个元素调用指定的lambda,然后我们在main函数中调用它。
值得注意的是,这里当我们的lambda是函数的最后一个参数时,我们可以将其写在()外面,当函数参数只有一个lambda时,可以直接省略()
还有第二个要注意的是,我们使用了匿名参数,当lambda只有一个参数时,我们可以不用显示的指定一个参数名,而是使用默认的it来引用。

扩展函数

kotlin中,我们可以很方便的扩展某个类,为其增加方法:

1
2
3
4
5
6
7
8
9
10
11
fun Int.compareWith(a: Int): Int {
return when {
this > a -> 1
this < a -> -1
else -> 0
}
}

fun main(vararg args: String) {
println(10.compareWith(4))
}

如上所示,声明一个扩展函数的语法很简单,只需要在方法名前面加上类名.,在方法中我们可以使用this来引用他,但是只能访问public的成员。这个类名我们使用receiver来描述它。
扩展函数只是kotlin中众多语法糖中的一个,他并没有真正的扩展这个类,只是将方法的一个参数提到了方法名前面作为receiver

1
2
3
fun Int.compareWith(a:Int):Int
===>
fun compareWith(this:Int,a:Int):Int

所以调用扩展函数和普通的函数调用没有区别,函数的receiver本质上还是这个函数的参数,而不是这个方法的所有者,因此在调用时使用的是静态分派,并不支持多态。
而且当扩展函数与类的方法冲突时,默认使用的是类的方法。

结合泛型的扩展函数
1
2
3
4
5
6
fun <T:Any> T?.toStr(){
println(this?.toString()?:"this is a null ref")
}
fun main(vararg args:String){
null.toStr()
}

正如上面的例子中所看到的,我们可以使用泛型参数作为函数的receiver,而且我们使用了T?,说明支持null

函数参数使用扩展函数

对刚刚的forEach函数稍加改造:

1
2
3
4
5
6
7
8
9
10
11
12
fun <T> forEach(list:List<T>,block:T.()->Unit){
for (t in list){
t.block()
}
}

fun main(vararg args:String){
var list = listOf(1,2,3,4,5)
forEach(list){
println(this)
}
}

注意在声明函数参数时,我们使用了T.()->Unit,也就是声明了一个具有receiverlambda