函数表达式
函数体只有一行代码时,可以省略{},省略return ,用=连接,return不用写
fun add(x:Int,y:Int) = x+y
fun say() = println("hello")
默认参数
定义函数参数时,可以给参数定义默认值
相当于java中的方法重载
fun hello(name: String = "张三") {
}
具名参数
函数被调用时,可以带上参数名称
可以指定参数的赋值顺序
需要把参数名称带上
fun say(name: String, age: Int) {
}
say(age = 1, name = "哈哈")
可变参数(vararg)
fun varargs(vararg i:Int){
for (k in i){
println(k)
}
}
参数列表中只能有一个vararg参数,如果该参数不是在最后一个参数位置,需要使用具名参数进行传递。
传递参数时,可以一个一个传递,如果是已有的数组进行传递时,可以使用操作符*
varargs(1,2,3)
//传递数组
val arr = intArrayOf(4,5,6)
varargs(*arr)
中缀函数
标有infix
关键字的函数可以使用中缀表达法调用。
中缀表达式的函数必须满足:
- 它们必须是
成员函数
或扩展函数
- 函数只能有一个参数
- 函数参数不能是可变参数且不能有默认值
//扩展函数
infix fun Int.add(i: Int): Int {
return this + i
}
val num = 100 add 2
//成员函数
class Infix{
infix fun say(name:String){
}
}
//调用
this say "hello"//正确
say("hello")//正确
//say ""//错误
中缀函数调用的优先级低于算术操作符、类型转换以及 rangeTo 操作符
val i = 90 add 10 * 2
val i2 = 90 add (10 * 2)
//这两种方式是等价的,中缀函数调用的优先级低
另一方面,中缀函数调用的优先级高于布尔操作符 && 与 ||、is- 与 in- 检测以及其他一些操作符
a && b xor c 等价于 a && (b xor c)
a xor b in c 等价于 (a xor b) in c
局部函数
可以在一个函数中定义另一个函数,局部函数可以访问外部函数的变量
fun test1() {
var name: String
fun test2(): String {
name = "hi"
return name
}
test2()
}
尾递归函数
每次递归都会让方法入栈,直到终止条件触发时,每个方法才返回,方法入栈次数多,占用内存多,如果方法层级过多,则会出现栈内存溢出
对递归做了优化,结合了递归和迭代的功能
尾递归的原理是,在kotlin中用递归的写法,底层java代码是使用迭代方式
既能比较方便的使用递归,又减少了对内存的开销
函数前面加上tailrec关键字
//尾递归
//尾递归优化加上tailrec
tailrec fun sum2(n: Int, sum: Int = 0): Int {
if (n <= 1) return sum + 1
return sum2(n - 1, sum + n)
}
函数必须将其自身调用作为它执行的最后一个操作。在递归调用后有更多代码时,不能使用尾递归,并且不能用在 try/catch/finally 块中
高阶函数
高阶函数是将函数用作参数或返回值的函数
函数类型
声明函数类型
使用(Int)->String
等一系列函数类型来处理函数的声明。Int表示函数类型的参数,String表示函数类型的返回值
- (Int)->Unit
- 带接收者A.(B)->C
- 挂起函数的使用suspend (String)->Unit或者suspend A.(B)->C
获取函数类型
- 匿名函数
fun(i:Int):String{}
val c: (Int) -> String = fun(i: Int): String {
val k = i + 10
return k.toString()
}
//核心就是:
(Int)->String = fun(i:Int):String{}
- lambda表达式
val d: (Int) -> String = { i: Int ->
""//最后一行表示返回值
}
//核心
(Int)->String = {i-> ""}
- 使用已有的函数引用,使用
::
进行引用
fun hi(i: Int): String {
return ""
}
val f: (Int) -> String = ::hi
//扩展函数
fun Boolean.check(i: Int): String {
return ""
}
fun check2(b: Boolean, i: Int): String {
return ""
}
val g: Boolean.(Int) -> String = Boolean::check
val h: Boolean.(Int) -> String = ::check2
val k: (Boolean, Int) -> String = Boolean::check
//上面的扩展函数和函数类型可以互相转换
- 自定义类实现函数类型接口
class Hero :(Int)->String{
override fun invoke(p1: Int): String {
return (p1-100).toString()
}
}
val e = Hero()
函数类型的调用
函数类型变量调用()
或invoke()
即可调用函数类型
f.invoke(x) 或者直接 f(x)
调用扩展函数类型,invoke的第一个参数是接收者类型或者用接收者.()
val my: Double.(String) -> Unit = fun Double.(s: String) {}
my.invoke(10.0, "my")
//或
90.9.my("my")
闭包
一个函数返回了一个内部函数,该内部函数引用了外部函数的相关参数和变量,我们把该返回的内部函数称为闭包
fun test():()->Unit{
var a = 10
return {
println(a)
a++
}
}
val test = test()//()->Unit
test()//10
test()//11
带接收者函数类型
A.(B)->C
如果函数的参数是带接收者带函数类型,默认持有隐式作用域this
fun foo4(c: String.(name:Boolean) -> Unit){
c.invoke("ooo",false)
}
foo4 {//有隐式this,并且有隐式it
if (it){
this+"000"
}else{
this+"111"
}
println(this)
}
使用扩展函数引用传递
fun foo4(c: String.(name:Boolean) -> Unit){
c.invoke("ooo",false)
}
foo4(fun String.(b:Boolean){//this表示String
}
)
//或者
fun String.ff(b:Boolean){//this
}
foo4(String::ff)
//或者
foo4{//this,it
}
有无接收者的函数类型可以互相引用
fun main() {
val v1: Boolean.(Int) -> String = Boolean::f1
//带接收者的类型相当于没有带接收者的第一个参数
val v2: (Boolean, Int) -> String = Boolean::f1
val v3: Boolean.(Int) -> String = ::f2
val v4: (Boolean, Int) -> String = ::f2
v4.invoke(true, 1)
true.v3(1)
v3.invoke(false, 2)
Boolean::f1.invoke(false, 20)
(Boolean::f1)(false, 20)
(::f2)(false, 1)
false.f1(10)
}
fun Boolean.f1(i: Int): String {
return ""
}
fun f2(b: Boolean, i: Int): String {
return ""
}
匿名函数
匿名函数是没有名字的常规函数,但只能用在表示函数类型,不能单独定义。
fun foo4(c: String.(name:Boolean) -> Unit){
c.invoke("ooo",false)
}
foo4(fun(n:String,b:Boolean){})
lambda表达式
lambda 表达式总是括在花括号中, 完整语法形式的参数声明放在花括号内,并有可选的类型标注, 函数体跟在一个 -> 符号之后。如果推断出的该 lambda 的返回类型不是 Unit,那么该 lambda 主体中的最后一个(或可能是单个) 表达式会视为返回值。
fun foo(s: String, callback: (Boolean) -> Unit) {
}
foo(""){}
如果函数的最后一个参数是函数类型,可以写到圆括号外面,如果函数参数只有一个函数类型的参数,可以省略圆括号
fun foo2(c:()->Unit){
}
foo2 {
}
lambda表达式只有一个参数时,有隐式参数it
,有多个参数时,需要显式声明。
lambda返回值是最后一行,不需要return
fun foo3(c: (Int) -> String){
}
foo3{
val i1 = it + 10//隐式it
i1.toString()//返回值
}