多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
# 函数 ## 函数声明 Kotlin 中函数使用 `fun` 关键字声明 ``` kotlin fun double(x: Int): Int { } ``` ## 函数用法 使用传统方法调用函数 ``` kotlin val result = double(2) ``` 使用点标记调用成员函数 ``` kotlin Sample().foo() // 创建 Sample 实例并调用 foo ``` ### infix 符号 在下列情况下函数还可以使用 `infix` 符号调用 * 成员函数或者 [扩展函数](extensions.html) * 有一个单独的参数 * 被 `infix` 关键字所标记 ``` kotlin // 定义 Int 扩展 infix fun Int.shl(x: Int): Int { ... } // 使用 infix 标记调用扩展函数 1 shl 2 // 含义与下面一样 1.shl(2) ``` ### 形参 函数形参使用 Pascal 标记法定义,例如:*name*: *type*。形参使用逗号分隔。每个参数必须有明确的类型。 ``` kotlin fun powerOf(number: Int, exponent: Int) { ... } ``` ### 默认参量 在相应的参量被省略的地方,函数参数可以有默认值。这会减少与其它语言的差异。 ``` kotlin fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size()) { ... } ``` > 使用紧接在类型后面的 **=** 定义默认值 ### 命名参量 调用函数时可以为函数参数命名。这非常适合于某些有很多参数或者有一个默认参数的函数。 请看下面给出的方法 ``` kotlin fun reformat(str: String, normalizeCase: Boolean = true, upperCaseFirstLetter: Boolean = true, divideByCamelHumps: Boolean = false, wordSeparator: Char = ' ') { ... } ``` 我们可以使用默认值调用 ``` kotlin reformat(str) ``` 然而,当要以非默认值调用它时,就得像这样: ``` kotlin reformat(str, true, true, false, '_') ``` 与命名参量一起我们可以让代码更具可读性 ``` kotlin reformat(str, normalizeCase = true, upperCaseFirstLetter = true, divideByCamelHumps = false, wordSeparator = '_' ) ``` 并且如果我们不需要所有的参量 ``` kotlin reformat(str, wordSeparator = '_') ``` >[danger]注意命名参量语法不能在调用 Java 方法时使用,因为 Java 字节码不会一直保持函数参数的名称。 ### 返回 `Unit` 的函数 如果一个函数不返回有用的值,它的返回类型就是 `Unit`。`Unit` 是一个仅有一个 `Unit` 值的类型。这个值不用明确地返回。 ``` kotlin fun printHello(name: String?): Unit { if (name != null) println("Hello ${name}") else println("Hi there!") // `return Unit` 或者 `return` 是可选的 } ``` 声明 `Unit` 返回类型同样也是可选的。上面的代码等同于下面 ``` kotlin fun printHello(name: String?) { ... } ``` ### 单表达式函数 当一个函数返回单个表达式时,可以省略花括号,函数体在一个 `=` 符号后指定 ``` kotlin fun double(x: Int): Int = x * 2 ``` 在编译器能推断时,明确地声明返回类型是[optional](#explicit-return-types)的。 Explicitly declaring the return type is [optional](#explicit-return-types) when this can be inferred by the compiler ``` kotlin fun double(x: Int) = x * 2 ``` ### 明确的返回类型 有其它块的函数必须明确地指定返回类型,除非是要它们返回 `Unit`,[which 流可选](#unit-returning-functions)。Kotlin 无法为有块体的函数推断出返回类型,因为这类函数的函数体可能有复杂的控制流,并且返回类型对于阅读器来说不太明确(编译器也无法理解)。 ### 可变数量的参量(Varargs) 函数的某个参数(一般是最后一个)可以被修饰语 `vararg` 一起被标记: ``` kotlin fun <T> asList(vararg ts: T): List<T> { val result = ArrayList<T>() for (t in ts) // ts is an Array result.add(t) return result } ``` 允许向函数传递可变数量的参量: ```kotlin val list = asList(1, 2, 3) ``` 类型为 `T` 的 `vararg` 参数在函数内部会以一个 `T` 数组呈现,例如在上面的示例中的 `ts` 变量的类型是 `Array<out T>`。 只有一个参数可以被标记为 `vararg`。如果一个 `vararg` 参数不是最后一个参数,那么后面的参数值可以使用命名参量语法,或者这个参数通过传递一个 lambda 外部插入而获得一个函数类型。 当我们调用一个 `vararg` 函数时,我们既可以一个一个地传递参量,例如 `asList(1, 2, 3)`,又或者,如果我们已经有了一个数组并想要把它的内容传递给函数,可以使用**展开**操作(给数组加上 `*` 前缀): ```kotlin val a = arrayOf(1, 2, 3) val list = asList(-1, 0, *a, 4) ``` ## 函数作用域 可以在文件顶层声明 Kotlin 函数,即你不需要创建一个类来保存一个函数,类似于像 Java, C# 或 Scala 一样的编程语言。除了顶层函数外,Kotlin 函数还可以和成员函数和扩展函数一样被声明为本地。 ### 本地函数 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()) } ``` 本地函数可以访问函数外的本地变量(例如闭包)。因此上面的例子中, `visited` 可以是一个本地变量 ``` kotlin 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]) } ``` ### 成员函数 成员函数是定义在某个类或对象内部的函数 ``` kotlin class Sample() { fun foo() { print("Foo") } } ``` 成员函数用句点标记调用 ``` kotlin Sample().foo() // 创建 Sample 实例并调用 foo ``` 更多关于类和覆盖成员的信息,查看[类](classes.html) 和[继承](classes.html#inheritance)章节 ## 泛型函数 函数可以有泛型参数,在函数名前面用尖括号指定。 ``` kotlin fun <T> singletonList(item: T): List<T> { // ... } ``` 更多关于泛型函数的信息查看[泛型](generics.html)章节 ## 内联函数 关于内联函数的解释在[这里](inline-functions.html)。 ## 扩展函数 关于扩展函数在[专门的章节](extensions.html)中解释。 ## 高阶函数与 Lambdas 高阶函数与 Lambdas 在 [专门的章节](lambdas.html)中解释 ## 尾部递归函数 Kotlin 支持一种叫做[尾部递归](https://en.wikipedia.org/wiki/Tail_call)的编程风格。这允许一些通常意义上的使用递归函数替代循环的算法,但不会有堆栈溢出的风险。 当一个函数被 `tailrec` 修饰并遇到符合的形式,编译器会优化递归,替代为一个快速并有效率的基于循环的版本。 ``` kotlin tailrec fun findFixPoint(x: Double = 1.0): Double = if (x == Math.cos(x)) x else findFixPoint(Math.cos(x)) ``` 这段代码为一个精确的常量计算其余弦的定点。它从 1.0 开始简单地重复调用 `Math.cos`,直到结果不再改变,产生了一个 0.7390851332151607 的结果。所产生等价的传统风格代码为: ``` kotlin private fun findFixPoint(): Double { var x = 1.0 while (true) { val y = Math.cos(x) if (x == y) return y x = y } } ``` `tailrec` 修饰语正确的用法是,一个函数调用自身必须是最后才能做的事情。你不能在递归之后还有更多代码时使用尾部递归,并且你不能与 `try`/`cathch`/`finally` 块一起使用。当前尾部递归仅支持 JVM 后端。