函数类型

函数

Kotlin 中的函数使用fun关键字声明,函数名称的命名规则与变量一致:

fun double(x: Int): Int {
    return 2 * x
}

函数作为类型的表示

Kotlin 的函数类型是一种特殊的类型,它可以描述函数的类型信息,包括函数的参数和返回值类型。在 Kotlin 中,函数类型是以 (参数类型) -> 返回值类型 的形式来表示的。

例如,一个函数类型为 (Int, String) -> Boolean 的函数类型表示接收两个参数,一个为 Int 类型,一个为 String 类型,返回一个 Boolean 类型的值。

在 Kotlin 中,函数类型可以用作变量类型、参数类型、返回值类型、以及类型别名等。这使得函数可以像普通变量一样被传递、赋值、以及作为函数的参数和返回值使用。这种特性在函数式编程中非常重要。

同时,Kotlin 还提供了一种特殊的函数类型 () -> Unit,它表示不接收任何参数,也不返回任何值的函数类型,类似于 Java 中的 void 类型。

函数的参数

函数参数使用 Pascal 表示法定义,即 name: type。参数用逗号隔开。 每个参数必须有显式类型:

  1. 有参函数定义:

    fun powerOf(number: Int, exponent: Int): Int { /*……*/ }
  2. 无参函数定义:

    fun powerOf(): Int { /*……*/ }
  3. 参数可以添加默认值:

    fun read(
        b: Array<Byte>, 
        off: Int = 0, 
        len: Int = b.size,
    ) { /*……*/ }
  4. 如果从基类中覆盖一个有参的并且填写了默认值的方法,那么子类在覆盖此方法时的定义,必须从签名中省略默认参数值;也就是说,覆盖方法会沿用被覆盖方法的参数默认值:

    open class A {
        open fun foo(i: Int = 10) { /*……*/ }
    }
    
    class B : A() {
        override fun foo(i: Int) { /*……*/ }  // 不能有默认值
    }
  5. 可变参数

    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
    }
    
    asList(1, 2, 3)
    // 或者使用伸展操作符,将数组展开
    val a = arrayOf(1, 2, 3)
    val list = asList(-1, 0, *a, 4)

函数的返回值

  1. 无返回值函数

    // 无返回值函数可以使用 Unit 类型来表示,也可以省略返回值类型。
    fun printHello() {
        println("Hello")
    }
    
    fun printWorld(): Unit {
        println("World")
    }
  2. 单返回值函数

    // 单个返回值函数可以使用任意类型来表示,如果没有显式指定返回值类型,则会根据函数体自动推断出返回值类型。
    fun add(a: Int, b: Int): Int {
        return a + b
    }
    
    fun sub(a: Int, b: Int) = a - b
  3. 多返回值函数

    // Kotlin 中没有直接支持多个返回值的语法,但可以使用 Pair、Triple 等数据类来返回多个值。
    fun getPerson(): Pair<String, Int> {
        return Pair("Alice", 18)
    }
    
    fun getUser(): Triple<String, Int, String> {
        return Triple("Bob", 20, "male")
    }

函数的调用

  1. 正常通过类型匹配调用

    fun foo(
        bar: Int = 0,
        baz: Int,
    ) : Int {
        println("bar=$bar baz=$baz")
        return bar + baz
    }
    // 正常通过类型匹配进行调用
    val r = foo(1, 2)
  2. 函数参数中,如果每个参数都有默认值,那么可以省略函数参数,或者省略部分函数参数

    fun foo1(x: Int = 1, y: Int = 2) {}
    foo1()  
    foo2(1) // 传入x=1
    foo3(y = 1) // 传入y=1,x使用默认值,这里需要使用具名参数
  3. 如果函数中有默认值的参数在无默认参数之后,这时候又不想传入默认值参数,就必须使用具名参数

    fun foo2(
        bar: Int = 0,
        baz: Int,
    ) : Int {
        println("bar=$bar baz=$baz")
        return bar + baz
    }
    foo2(baz = 2)
  4. 同样的,如果所有没有默认值的参数都在前面,那么有默认值的参数都可以省略:

    fun reformat(
        str: String,
        normalizeCase: Boolean = true,
        upperCaseFirstLetter: Boolean = true,
        divideByCamelHumps: Boolean = false,
        wordSeparator: Char = ' ',
    ) {
    /*……*/
    }
    reformat('This is a long String!')

函数的调用,在没有具名参数的情况下,优先使用函数类型匹配函数。

局部函数

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])
}

匿名函数

val sum = fun(x: Int, y: Int): Int {
    return x + y
}
val s = sum(1, 2)
println(s)

// list的map方法,就可以传入一个匿名函数
val list = listOf(1, 2, 3, 4, 5)
val newList = list.map(fun(i): Int { return i * 2 })

闭包

fun returnFun(): () -> Int {
    var count = 0
    return { count++ }
}

fun main() {
    val function = returnFun()
    val function2 = returnFun()
    println(function()) // 0
    println(function()) // 1
    println(function()) // 2
    
    println(function2()) // 0
    println(function2()) // 1
    println(function2()) // 2
}

returnFun返回了一个函数,这个函数没有入参,返回值是Int。可以用变量接收它,还可以调用它。function和function2分别是创建的两个函数实例。 可以看到,每调用一次function(),count都会加一,说明count 被function持有了而且可以被修改。而function2和function的count是独立的,不是共享的。

最后更新于