Kotlin 函数必须用 fun 关键字开头,后面紧跟着函数名,以及一对小括号,小括号中是函数参数列表,如果函数有返回值,在小括号后面加冒号 (:),冒号后面是函数的返回值类型。
fun double(x: Int): Int { return 2 * x }
调用函数使用传统方法:
val result = double(2)
调用成员函数使用点表示法:
Stream().read() // 创建类 Stream 实例并调用 read()使用中缀标记法调用函数
Kotlin 允许使用中缀表达式调用函数。所谓中缀表达式,就是指将函数名称放到两个 *** 作数中间。这两个 *** 作数,左侧是包含函数的对象或值,右侧是函数的参数值。从这个描述可以看出,并不是所有的函数都支持中缀表达式。支持中缀标记法调用的函数必须满足下面3个条件。
- 成员函数或者扩展函数。
- 只有一个参数。
- 使用 infix 关键字声明函数。
下面举一个例子,在这个例子中,使用扩展给 String 类添加一个除法 *** 作。什么是字符串的除法 *** 作呢?这里的字符串的除法实际上也是一个语法糖,就是去除分子字符串中包含的所有分母字符串
infix fun String.div(str: String): String { return this.replace(str, "") }
如果按照一般的方式调用,那么可以通过下面的方法调用
fun main(args: Array) { var str = "Hello world" println(str.div("l")) // 输出:Heo word }
如果使用中缀表达式调用,那么写法如下:
fun main(args: Array) { var str = "Hello world" println(str div "l") // 输出:Heo word }
同时,中缀表达式还可以连续使用
fun main(args: Array) { var str = "Hello world" println(str div "l") //输出:Heo word println(str div "l" div "o") //输出:He wrd }
**注:**中缀函数调用的优先级低于算术 *** 作符、类型转换以及 rangeTo *** 作符。 以下表达式是等价的:
- 1 shl 2 + 3 等价于 1 shl (2 + 3)
- 0 until n * 2 等价于 0 until (n * 2)
- xs union ys as Set<*> 等价于 xs union (ys as Set<*>)
另一方面,中缀函数调用的优先级高于布尔 *** 作符 && 与 ||、is- 与 in- 检测以及其他一些 *** 作符。这些表达式也是等价的:
- a && b xor c 等价于 a && (b xor c)
- a xor b in c 等价于 (a xor b) in c
完整的优先级层次结构请参见其语法参考。
单表达式函数当函数返回单个表达式时,可以省略花括号并且在 = 符号之后指定代码体即可:
fun double(x: Int): Int = x * 2
当返回值类型可由编译器推断时,显式声明返回类型是可选的:
fun double(x: Int) = x * 2函数参数和返回值 可变参数
函数的参数( 一般是最后一个参数)可以标记为 vararg ,这样会将该参数作为可变参数处理。所谓可变参数,就是指可以任意多个参数,在函数内部,会按数组来处理这些参数值。下面的 asList 函数是一个泛型函数,该函数只有一个参数,井且是可变参数。该函数返回 List
funasList(vararg ts:T):List { var result=ArrayList () for (t in ts){ result.add(t) } return result }
由于 ts 是可变参数,因此可以传递任意多个参数值,并且可以是任意类型的。
fun main(args: Array) { var result = asList(0, "hello", 1, 2, 3, 4, 5,"world") println("result = [${result}]") }
在 asList 函数内部,类型为 vararg 参数会被看作一个 T 类型的数组,也就是说,asList 函数中的 ts 变量的类型为 Array
只有一个参数可以标记为 vararg 。如果 vararg 参数不是函数的最后一个参数,那么对于 vararg 参数之后的其他参数,可以使用命名参数语法来传递参数值,或者,如果参数类型是函数,可以在括号之外传递一个 Lambda 表达式。例如,下面的 asList 函数有3个参数,第1个参数是可变参数,后两个是 value1 和 value2 参数。由于最后一个参数不是可变参数,因此在传递 value1 和 value2 参数的值时需要使用命名参数。其中 Lambda 表达式会在后面详细介绍。
funasList(vararg ts: T, value1: Int, value2: String): List { var result = ArrayList () for (t in ts) { result.add(t) } println("value1 = [${value1}], value2 = [${value2}], result = [${result}]") return result } fun main(args: Array ) { println(product) var result = asList(1, 2, 3, 4, 5, "world", value1 = 0, value2 = "hello") println("result = [${result}]") // 输出: value1 = [0], value2 = [hello], result = [[1, 2, 3, 4, 5, world]] }
调用一个存在 vararg 参数的函数时,我们可以逐个传递参数值,如 asList(1,2,3) ,或者,如果我们已经有了一个数组,希望将它的内容传递给函数,可以使用展开 (spread) *** 作符( 在数组之前加一个* ):
fun main(args: Array返回值类型) { var a = arrayOf(0, "hello", "world", 1) asList(-1, 3, *a, value1 = 87, value2 = "98") //输出:value1 = [87], value2 = [98], result = [[-1, 3, 0, hello, world, 1]] }
如果函数体为多行语句组成的代码段,那么就必须明确指定返回值类型,除非这个函数返回 Unit (不返回任何值),这时返回类型的声明可以省略。对于多行语句组成的函数, Kotlin 不会推断其返回值类型,因为这样的函数内部可能存在复杂的控制流,而且返回值类型对于代码的阅读者来说并不是那么一目了然(有些时候,甚至对于编译器来说也很难判定返回值类型)。
函数的作用域在 Kotlin 中,函数可以定义在源代码的顶级范围内 (top level) ,这就意味着你不必像在 Java 、
C# 或 Scala 等语言中那样,创建一个类来容纳这个函数,除顶级函数外,Kotlin 中的函数也可以定义为局部函数、成员函数及扩展函数。
局部函数Kotlin 支持局部函数,也就是嵌套在另一个函数内的函数。
fun saveFile() { // 局部函数 fun getFullFileName(fn: String): String { return "/Users/$fn" } var fileName = getFullFileName("test.txt") println("$fileName 已经保存成功") // 输出:/Users/test.txt 已经保存成功 }
局部函数可以访问外部函数中的局部变量, 因此,在上面的例 中,fn 可以定义为一个局部变量。
fun saveFile() { var fn = "test.txt" // 局部函数 fun getFullFileName(): String { return "/Users/$fn" } var fileName = getFullFileName() println("$fileName 已经保存成功") // 输出:/Users/test.txt 已经保存成功 }成员函数
成员函数是在类或对象内部定义的函数:
class Sample { // 成员函数 fun foo() { print("Foo")} }
成员函数以点表示法调用:
Sample().foo() // 创建类 Sample 实例并调用 foo泛型函数
函数可以有泛型参数,通过在函数名前使用尖括号指定:
funsingletonList(item: T): List { }
关于泛型函数的更多信息参见泛型。
内联函数使用高阶函数会带来一些运行时的效率损失:每一个函数都是一个对象,并且会捕获一个闭包。 即那些在函数体内会访问到的变量。 内存分配(对于函数对象和类)和虚拟调用会引入运行时间开销。
但在很多情况下,通过将 Lambda 表达式内联在使用处,可以消除这些运行时消耗。要想让函数支持内联,需要在定义函数时使用 inline 关键字。
关于内联函数,可以查看这篇文章 。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)