Kotlin Reference: Functions
Contents
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
…
}[/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
如果一个函数并不返回任何有用的值,则它的返回类型是 Unit
。Unit
类型只有一个值——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
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]
在函数内部,一个类型为 T
的 vararg
参数表现为一个 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> singletonList(item: T): List<T> {
// …
}[/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 平台支持尾递归。