Kotlin的函数可以定义在文件顶部,也就是说不需要创建一个类来持有一个函数。另外,Kotlin函数还包括:本地函数、成员函数、扩展函数、内联函数、高阶函数、泛型函数、递归函数。
一、本地函数
本地函数是函数内部包含另一个函数,也就是函数嵌套,示例代码如下:
fun dfs(graph: Graph) {
fun dfs(current: Vertex, visited: Set<Vertex>) {
if (!visited.add(current)) return
for (v in current.neighbors)
dfs(v, visited)
}
dfs(graph.vertices[0], HashSet())
}
本地函数可以访问外部函数的本地变量,因此上述例子可以这样写:
fun dfs(graph: Graph) {
val visited = HashSet<Vertex>()
fun dfs(current: Vertex) {
if (!visited.add(current)) return
for (v in current.neighbors)
dfs(v)
}
dfs(graph.vertices[0])
}
二、成员函数
成员函数是定义在类内部,与Java一样,示例代码如下:
class Sample() {
fun hello() { print("Hello World") }
}
成员函数可以使用点引用来调用:
Sample().hello()
三、泛型函数
泛型函数是指函数中,参数或返回值类型定义成泛型,使用T表示:
fun <T> singletonList(item: T): List<T> {
print("This is generic function")
}
四、内联函数
内存分配(包含函数对象和类)和虚函数调用,会带来运行时开销。但是,这些开销可以通过内联的lambda表达式来消除。对于编译器来说,我们需要使用inline修饰符来标识:
inline fun <T> lock(lock: Lock, body: () -> T): T {
print("This is an inline function")
}
内联修饰符会影响函数本身和lambda传递,这些都要内联到调用端。内联可能会引起代码增长。但是,如果我们使用恰当(避免在庞大函数中使用),它可以提升性能。
1、noinline
如果,我们只想某个lambda表达式内联,而不是整个函数内联。我们可以使用inlined和ontInlined修饰符来标识:
inline fun hello(inlined: () -> Unit, noinline notInlined: () -> Unit) {
print("This is inlined lambda")
}
2、crossinline
有些函数传递lambda参数不是通过函数体本身,而是从另一个执行体,比如本地对象或者嵌套函数。这种情况下,non-local控制流也不允许出现在lambda表达式中。这时,需要使用crossline修饰符来标识:
inline fun f(crossinline body: () -> Unit) {
val f = object: Runnable {
override fun run() = body()
}
}
3、inline properties
内联修饰符可以用于属性的访问,我们可以这样注解单个属性:
val foo: Foo
inline get() = Foo()
var bar: Bar
get() = ...
inline set(v) { ... }
五、扩展函数
Kotlin提供类的扩展功能,不必用继承类的方法或者使用装饰者的设计模式。可以通过extensions来特殊声明,Kotlin支持扩展函数和扩展属性。声明一个扩展函数,我们需要在名称前面加一个接收类型。如下代码是往MutableList<Int>添加swap功能:
fun MutableList<Int>.swap(index1: Int, index2: Int) {
val tmp = this[index1] // 'this' corresponds to the list
this[index1] = this[index2]
this[index2] = tmp
}
扩展函数中的this关键字对应接收者类型。现在,我们可以调用MutableList<Int>中的swap方法:
val l = mutableListOf(1, 2, 3)
l.swap(0, 2)
六、高阶函数
高阶函数是把函数作为参数或者返回值的函数。示例代码如下:
fun <T, R> Collection<T>.fold(
initial: R,
combine: (acc: R, nextElement: T) -> R
): R {
var accumulator: R = initial
for (element: T in this) {
accumulator = combine(accumulator, element)
}
return accumulator
}
七、递归函数
Kotlin支持特色功能性编程——尾递归。可以运行算法使用loop循环来代替递归函数,这样可以避免栈溢出。当使用tailrec修饰符时,遇到特定格式,编译器会优化递归,改成快速高效的基于loop循环的版本。示例代码如下:
tailrec fun findFixPoint(x: Double = 1.0): Double
= if (Math.abs(x - Math.cos(x)) < eps) x else findFixPoint(Math.cos(x))