# 函数类型

## 函数

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

```kotlin
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. 有参函数定义：

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

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

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

   ```kotlin
   open class A {
       open fun foo(i: Int = 10) { /*……*/ }
   }

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

   ```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
   }

   asList(1, 2, 3)
   // 或者使用伸展操作符，将数组展开
   val a = arrayOf(1, 2, 3)
   val list = asList(-1, 0, *a, 4)
   ```

### 函数的返回值

1. 无返回值函数

   ```kotlin
   // 无返回值函数可以使用 Unit 类型来表示，也可以省略返回值类型。
   fun printHello() {
       println("Hello")
   }

   fun printWorld(): Unit {
       println("World")
   }
   ```
2. 单返回值函数

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

   fun sub(a: Int, b: Int) = a - b
   ```
3. 多返回值函数

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

   fun getUser(): Triple<String, Int, String> {
       return Triple("Bob", 20, "male")
   }
   ```

### 函数的调用

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

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

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

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

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

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

### 局部函数

Kotlin 支持局部函数，即一个函数在另一个函数内部，局部函数可以访问外部函数（即闭包）的局部变量：

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

## 闭包

```kotlin
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是独立的，不是共享的。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://yangsx95.gitbook.io/notes/programming-language/kotlin/shu-ju-lei-xing/han-shu-lei-xing.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
