Kotlin学习(七):函数

Kotlin学习(七):函数

函数基本用法

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<String>) {
    
    
    var str = "Hello world"
    println(str.div("l")) // 输出:Heo word
}

如果使用中缀表达式调用,那么写法如下:

fun main(args: Array<String>) {
    
    
    var str = "Hello world"
    println(str div "l") // 输出:Heo word
}

同时,中缀表达式还可以连续使用

fun main(args: Array<String>) {
    
    
    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<T> 类型。 asList 函数的功能是将一组值转换为List<T> 对象,并返回该对象。

fun <T> asList(vararg ts:T):List<T>{
    
    
    var result=ArrayList<T>()
  for (t in ts){
    
    
      result.add(t)
  }
    return result
}

由于 ts 是可变参数,因此可以传递任意多个参数值,并且可以是任意类型的。

fun main(args: Array<String>) {
    
    
    var result = asList(0, "hello", 1, 2, 3, 4, 5,"world")
    println("result = [${
      
      result}]")
}

asList 函数内部,类型为 vararg 参数会被看作一个 T 类型的数组,也就是说,asList 函数中的 ts 变量的类型为 Array<out T>

只有一个参数可以标记为 vararg 。如果 vararg 参数不是函数的最后一个参数,那么对于 vararg 参数之后的其他参数,可以使用命名参数语法来传递参数值,或者,如果参数类型是函数,可以在括号之外传递一个 Lambda 表达式。例如,下面的 asList 函数有3个参数,第1个参数是可变参数,后两个是 value1value2 参数。由于最后一个参数不是可变参数,因此在传递 value1value2 参数的值时需要使用命名参数。其中 Lambda 表达式会在后面详细介绍。

fun <T> asList(vararg ts: T, value1: Int, value2: String): List<T> {
    
    
    var result = ArrayList<T>()
    for (t in ts) {
    
    
        result.add(t)
    }
    println("value1 = [${
      
      value1}], value2 = [${
      
      value2}], result = [${
      
      result}]")
    return result
}

fun main(args: Array<String>) {
    
    
    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<String>) {
    
    
    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

泛型函数

函数可以有泛型参数,通过在函数名前使用尖括号指定:

fun <T> singletonList(item: T): List<T> { /*……*/ }

关于泛型函数的更多信息参见泛型

内联函数

使用高阶函数会带来一些运行时的效率损失:每一个函数都是一个对象,并且会捕获一个闭包。 即那些在函数体内会访问到的变量。 内存分配(对于函数对象和类)和虚拟调用会引入运行时间开销。

但在很多情况下,通过将 Lambda 表达式内联在使用处,可以消除这些运行时消耗。要想让函数支持内联,需要在定义函数时使用 inline 关键字。

关于内联函数,可以查看这篇文章

猜你喜欢

转载自blog.csdn.net/u010812857/article/details/121563125