Kotlin Reference: Functions

Function Declarations

  在 Kotlin 中使用 fun 关键字定义函数:

[code lang=”kotlin”]fun double(x: Int): Int {
return 2*x
}[/code]

Function Usage

  函数的调用和传统方法一样:

[code lang=”kotlin”]val result = double(2)[/code]

  使用点表示法(.)调用成员函数:

[code lang=”kotlin”]Sample().foo() // create instance of class Sample and calls foo[/code]

Infix notation

  满足以下条件时,也可以使用中缀表示法调用函数:

  • 是成员函数或扩展函数
  • 只有一个参数
  • 使用 infix 关键字标记

[code lang=”kotlin”]// Define extension to Int
infix fun Int.shl(x: Int): Int {

}

// call extension function using infix notation

1 shl 2

// is the same as

1.shl(2)[/code]

Parameters

  函数的参数使用 Pascal 表示法定义,即 name: type,参数之间使用逗号分隔,必须显式地为每个参数指定类型。

[code lang=”kotlin”]fun powerOf(number: Int, exponent: Int) {

}[/code]

Default Arguments

  函数的参数可以具有默认值,当调用函数时省略了某个参数,则该参数就会使用默认值。与其他语言相比,这样可以减少很多重载方法。

[code lang=”kotlin”]fun read(b: Array, off: Int = 0, len: Int = b.size()) {

}[/code]

  在参数的类型后面添加 = 和值来为参数定义默认值。

  覆盖方法总与基类方法具有相同的默认参数值,在覆盖带有默认参数值的方法时,必须在签名中省略默认参数值:

[code lang=”kotlin”]open class A {
open fun foo(i: Int = 10) { … }
}

class B : A() {
override fun foo(i: Int) { … } // no default value allowed
}[/code]

Named Arguments

  在调用函数时,可以命名函数的参数,这在函数具有大量参数或默认参数的时候非常方便。

  比如对于下面的函数:

[code lang=”kotlin”]fun reformat(str: String,
normalizeCase: Boolean = true,
upperCaseFirstLetter: Boolean = true,
divideByCamelHumps: Boolean = false,
wordSeparator: Char = ‘ ‘) {

}[/code]

  可以使用默认参数来调用:

[code lang=”kotlin”]reformat(str)[/code]

  也可以不使用默认参数,就像:

[code lang=”kotlin”]reformat(str, true, true, false, ‘_’)[/code]

  使用命名参数可以提高代码的可读性:

[code lang=”kotlin”]reformat(str,
normalizeCase = true,
upperCaseFirstLetter = true,
divideByCamelHumps = false,
wordSeparator = ‘_’
)[/code]

如果不需要修改所有的参数:

[code lang=”kotlin”]reformat(str, wordSeparator = ‘_’)[/code]

  注意命名参数的语法不能用于调用 Java 方法,因为 Java 的字节码并不总会保留函数的参数名称。

Unit-returning functions

  如果一个函数并不返回任何有用的值,则它的返回类型是 UnitUnit 类型只有一个值——Unit,这个值不需要被显式地返回:

[code lang=”kotlin”]fun printHello(name: String?): Unit {
if (name != null)
println(“Hello ${name}”)
else
println(“Hi there!”)
// return Unit or return is optional
}[/code]

  Unit 返回类型的声明是可选的,下面代码的函数声明和上面等效:

[code lang=”kotlin”]fun printHello(name: String?) {

}[/code]

Single-Expression functions

  如果一个函数仅返回一个表达式,则可以省略大括号,把函数体直接写在 = 后面:

[code lang=”kotlin”]fun double(x: Int): Int = x * 2[/code]

  当编译器可以推断出类型时,返回值类型的声明是可选的:

[code lang=”kotlin”]fun double(x: Int) = x * 2[/code]

Explicit return types

  以代码块作为函数体的函数必须始终显式地指定返回值的类型,除非函数返回的是 Unit此时可以省略返回值类型。Kotlin 不会推断以代码块作为函数体的函数的返回值类型,因为此类函数往往有复杂的控制流,其返回值类型对读者来说并不明显(有时对编译器也是如此)。

Variable number of arguments (Varargs)

  函数的参数(通常是最后一个)可以使用 vararg 修饰符标记:

[code lang=”kotlin”]fun asList(vararg ts: T): List {
val result = ArrayList()
for (t in ts) // ts is an Array
result.add(t)
return result
}[/code]

  vararg 允许向函数传递可变数量的参数:

[code lang=”kotlin”]val list = asList(1, 2, 3)[/code]

  在函数内部,一个类型为 Tvararg 参数表现为一个 T 型的数组,也就是说,上面代码中 ts 的类型为 Array<out T>

  只能有一个参数被标记为 vararg。如果 vararg 参数不是参数列表中的最后一个参数,则 vararg 参数后的其他参数可以以命名参数的形式传递,或者如果参数是函数类型,可以以括号外 Lambda 表达式的形式传递。

  在调用具有可变参数的函数时,我们可以逐一地传递参数,如 asList(1, 2, 3)。如果想把一个已有数组的内容传递给函数,可以使用传播(Spread)操作符(数组前缀 *):

[code lang=”kotlin”]val a = arrayOf(1, 2, 3)
val list = asList(-1, 0, *a, 4)[/code]

Function Scope

  在 Kotlin 中,可以在文件的顶层声明函数,这意味着不需要像 Java、C#、Scala 等语言一样创建类来持有函数。除了顶层函数,Kotlin 的函数还可以声明为局部函数、作为成员函数或者扩展函数。

Local Functions

  Kotlin 支持局部函数,也就是在函数内的函数。

[code lang=”kotlin”]fun dfs(graph: Graph) {
fun dfs(current: Vertex, visited: Set) {
if (!visited.add(current)) return
for (v in current.neighbors)
dfs(v, visited)
}

dfs(graph.vertices[0], HashSet())
}[/code]

  局部函数可以访问外部函数的局部变量(即闭包,Closure),所以在上面的例子里,可以把参数 visited 声明为局部变量:

[code lang=”kotlin”]fun dfs(graph: Graph) {
val visited = HashSet()
fun dfs(current: Vertex) {
if (!visited.add(current)) return
for (v in current.neighbors)
dfs(v)
}

dfs(graph.vertices[0])
}[/code]

Member Functions

  成员函数是定义在类或 object 中的函数。

[code lang=”kotlin”]class Sample() {
fun foo() { print(“Foo”) }
}[/code]

  使用点(.)表示法来调用成员函数:

[code lang=”kotlin”]Sample().foo() // creates instance of class Sample and calls foo[/code]

  关于类和覆盖成员的更多内容,请参考继承

Generic Functions

  函数可以有泛型参数,在函数名前面使用尖括号指明泛型参数。

[code lang=”kotlin”]fun &ltT&gt singletonList(item: T): List&ltT&gt {
// …
}[/code]

  关于泛型的更多内容,请参考 Generics

Inline Functions

  内联函数的具体解释见这里

Extension Functions

  扩展函数的具体解释在这里

Higher-Order Functions and Lambdas

  高阶函数和 Lambda 表达式的具体解释见这里

Tail recursive functions

  Kotlin 支持尾递归(Tail Recursion)的函数式编程风格,可以用递归的形式实现一些通常被写为循环的算法,而不必担心栈溢出。当函数被标记为 tailrec 且满足特定要求时,编译器会把递归优化为快速高效的循环。

[code lang=”kotlin”]tailrec fun findFixPoint(x: Double = 1.0): Double
= if (x == Math.cos(x)) x else findFixPoint(Math.cos(x))[/code]

上面的函数计算了余弦函数的不动点,是一个数学常量,只是从 1.0 开始反复调用 Math.cos,直到结果不再变化,输出的结果为 0.7390851332151607。上面的代码等效于下面这段传统风格的代码:

[code lang=”kotlin”]private fun findFixPoint(): Double {
var x = 1.0
while (true) {
val y = Math.cos(x)
if (x == y) return y
x = y
}
}[/code]

  要使用尾递归,除了使用 tailrec 修饰符,还要求函数在其最后一步操作中调用自身,如果在自身调用之后还有其他代码,就不能使用尾递归。尾递归不能用于 try/catch/finally 块中。目前仅 JVM 平台支持尾递归。