kotlin支持函数时编程的尾递归。这个允许一些算法可以通过循环而不是递归解决问题,从而避免了栈溢出。当函数被标记为tailrec
时,编译器会优化递归,并用高效迅速的循环代替它
//尾递归
tailrec fun findFixPoint(x:Double=1.0):Double=if (x==Math.cos(x))x else findFixPoint(Math.cos(x))
这是计算余弦不动点,输出结果
0.7390851332151607
**tailrec:**符号使用后,最后一个操作只能是调用函数自己,
不允许在递归代码后不允许有其他代码的
不允许在try/catch/finally块中
当前的尾递归只能在JV买的后端中用
高阶函数就是可以接受函数作为参数或者返回一个函数的函数。
比如lock()
就是例子,接受一个lock对象和一个函数,运行函数并释放lock
//高阶函数
fun <T> lock(lock: Lock,body:()->T):T{
lock.lock()
try {
return body()
}
finally {
lock.unlock()
}
}
如果我们想调用lock()
函数,我们可以传给他另外一个函数作为参数
fun toBeSynchroized() = sharedResource.operation()
val result = lock(lock, ::toBeSynchroized)
其中最方便的办法是传递一个字面函数(通常为lambda表达式):
val result=lock(lock,{sharedResource.operation()})
字面函数被包括在大括号里
参数在
->
前面声明(参数类型可以省略)函数体在
->
之后
lock(lock){
sharedResource.operation()
}
map()
(ofMapReduce):fun <T,R> List<T>.map(transform:(T)->R):List<R>{
val result= arrayListOf<R>()
for (item in this){
result.add(transform(item))
}
return result
}
val doubled= arrayListOf(1,2,3).map { it->it*2 }
println(doubled)
如果字面只有一个参数,声明可以省略,名字叫就是it
:
val doubled= arrayListOf(1,2,3).map { it*2 }
println(doubled)
输出结果
[2, 4, 6]
并且可以写LINQ-风格的代码
val strings= arrayOf("a","abc","abcd","qww")
val test=strings.filter { it.length==3 }.sortedBy { it }.map { it.toUpperCase() }
for (ite in test){
println(ite)
}
输出
ABC
QWW
**内联函数时为了提高高阶函数的性能:**使用高阶函数会带来一些运行时的效率损失:每一个函数都是一个对象,且这个对象捕获了一个闭包,闭包内的变量可以在函数对象内部访问。内存分配(为函数对象和类)和实际调用将引入运行时间开销。
但通过使用内联lambda表达式
,可以避免这些情况出现。
当一个函数被声明inline
时,它的函数体是内联的,也就是说,函数体会被直接替换到函数调用的地方,如下例子:
//内联函数
inline fun inlineTest(prefix:String,action:()->Unit){
println("call before $prefix")
action()
println("call after $prefix")
}
//内联函数
inlineTest("douwowan"){
println("hahhah")
}
输出
call before douwowan
hahhah
call after douwowan
上面所述代码就相当于直接输出——编译器编译时显示成这样:
println("call before douwowan")
action()
println("call after douwowan")
val call:()->Unit={ println("hahaha")}
inlineTest("都一样",call)
inlineTest("差不多",{println("hahaha")})
输出
call before 都一样
hahaha
call after 都一样
call before 差不多
hahaha
call after 差不多
函数参数是用Pascal符号定义的 name:type
。参数之间用都好隔开,每个参数必须指明类型。
fun powerOf(number:Int,exponent:Int){}
函数参数可以设置默认值,当参数被忽略时会使用默认值,
fun read(b:Array<Byte>,off:Int=0,len:Int=b.size){}
默认值可以通过type类型后使用=
进行赋值
在调用函数时可以参数命名,这对于那种有大量参数的函数很方便
fun reformat(str:String,isMale:Boolean=true,age:Int=27){...}
TestResourceLoader().reformat("wyc")
使用时可以使用默认的参数来初始化方法不用过分重载,适用于某些含有默认属性的方法
如果函数不会反悔任何有用值,那么他的返回类型就是unit
.并且可以省略
fun printHello(name: String?): Unit {
if (name != null)
println("Hello ${name}")
else
println("Hi there!")
// `return Unit` or `return` is optional
}
当函数只返回单个表达式,大括号可以省略并在=
后面定义函数体
fun double(x:Int):Int=X*2
在编译器可以推断出返回值类型的时候,返回值可以省略
fun double(x:Int)=X*2
下面的例子中必须有明确返回类型,除非他是返回Unit
类型。kotlin并不会对函数体重的返回类型进行推断,因为函数体中可能有复杂的控制流,他的返回类型未必对读者课件(甚至编译器而言也有可能是不可见的)
函数的参数(通常是最后一个参数)可以用vararg
修饰符进行标记:
//可变长度参数
fun <T> asList(vararg ts:T):List<T>{
val result=ArrayList<T>()
result.addAll(ts)
return result
}
println(asList(1,2,3,"wyc"))
输出结果
[1, 2, 3, wyc]
标记后允许传递可变长度的参数
vararg
。加入vararg
并不是列表中的最后一个参数,那么后面的参数需要通过命名参数语法进行传值,再或者如果这个参数是函数类型,就需要通过lambda法则。当调用变长参数的函数时,我们可以一个一个传递参数,比如asList(1,2,3)
,或者我们传递一个array的内容给函数,我们就可以使用*
前缀操作符:
val list= arrayOf("q","w","e","r")
val changeResult= asList(1,2,3,"wyc",*list)
println(changeResult)