09. 函数(高级篇)

高阶函数

高阶函数是以另一个函数作为参数 and/or 返回函数的函数。

将函数作为参数

1
2
3
4
5
6
7
8
9
10
11
fun calculate(x: Int, y: Int, operation: (Int, Int) -> Int): Int {  // 1
return operation(x, y) // 2
}

fun sum(x: Int, y: Int) = x + y // 3

fun main() {
val sumResult = calculate(4, 5, ::sum) // 4
val mulResult = calculate(4, 5) { a, b -> a * b } // 5
println("sumResult $sumResult, mulResult $mulResult")
}
  1. 声明一个高阶函数。它接受两个整数参数 x 和 y。此外,它还接受另一个函数操作作为参数。声明中还定义了操作参数和返回类型。
  2. 高阶函数返回包含所提供参数的操作调用结果。
  3. 声明与操作签名匹配的函数。
  4. 调用高阶函数传入两个整数值和函数参数 ::sum::s 是在 Kotlin 按名称引用函数的符号。
  5. 调用传入 lambda 的高阶函数作为函数参数,看起来会更清楚。

返回函数

1
2
3
4
5
6
7
8
9
10
fun operation(): (Int) -> Int {                                     // 1
return ::square
}

fun square(x: Int) = x * x // 2

fun main() {
val func = operation() // 3
println(func(2)) // 4
}
  1. 声明一个返回函数的高阶函数。(Int)-> Int 表示 square 函数的参数和返回类型。
  2. 声明与签名匹配的函数。
  3. 调用 operation 来获取赋值给变量的结果。这里 func 变成了由 operation 返回的 square。
  4. 调用 func,实际执行 square 函数。

Lambda 函数

Lambda 函数(“lambdas”)是创建临时函数的一种简单方法。 由于类型推断和隐式 it 变量,在许多情况下可以非常简洁地表示Lambda。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// All examples create a function object that performs upper-casing.
// So it's a function from String to String

val upperCase1: (String) -> String = { str: String -> str.uppercase() } // 1

val upperCase2: (String) -> String = { str -> str.uppercase() } // 2

val upperCase3 = { str: String -> str.uppercase() } // 3

// val upperCase4 = { str -> str.uppercase() } // 4

val upperCase5: (String) -> String = { it.uppercase() } // 5

val upperCase6: (String) -> String = String::uppercase // 6

println(upperCase1("hello"))
println(upperCase2("hello"))
println(upperCase3("hello"))
println(upperCase5("hello"))
println(upperCase6("hello"))
  1. 一个光荣的 lambda,到处都是显式类型。Lambda 是大括号中的部分,它被赋给一个类型为 (String) -> String (函数类型)的变量。
  2. Lambda 内部的类型推断: lambda 参数的类型是从它所赋值的变量的类型推断出来的。
  3. Lambda 之外的类型推断: 变量的类型是从 lambda 参数的类型和返回值推断出来的。
  4. 您不能同时进行这两项操作,编译器就没有机会以这种方式推断类型。
  5. 对于只有一个参数的 lambdas,您不必显式地为其命名。相反,您可以使用隐式 it 变量。当可以推断出它的类型时(通常是这种情况) ,这尤其有用。
  6. 如果您的 lambda 由单个函数调用组成,您可以使用函数指针( :: )。

Infix Functions 中缀函数

具有单个参数的成员函数和扩展可以转换为中缀函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
fun main() {

infix fun Int.times(str: String) = str.repeat(this) // 1
println(2 times "Bye ") // 2

val pair = "Ferrari" to "Katrina" // 3
println(pair)

infix fun String.onto(other: String) = Pair(this, other) // 4
val myPair = "McLaren" onto "Lucas"
println(myPair)

val sophia = Person("Sophia")
val claudia = Person("Claudia")
sophia likes claudia // 5
}

class Person(val name: String) {
val likedPeople = mutableListOf<Person>()
infix fun likes(other: Person) { likedPeople.add(other) } // 6
}
  1. 在 Int 上定义中缀扩展函数。
  2. 调用中缀函数。
  3. 通过从标准库调用中缀函数来创建一个副。
  4. 下面是您自己创造性地调用的实现。
  5. 中缀表示法也适用于成员函数(方法)。
  6. 包含类成为第一个参数。

运算符函数

某些函数可以“升级”为运算符,允许它们使用相应的运算符符号进行调用。

1
2
3
4
5
6
operator fun Int.times(str: String) = str.repeat(this)       // 1
println(2 * "Bye ") // 2

operator fun String.get(range: IntRange) = substring(range) // 3
val str = "Always forgive your enemies; nothing annoys them so much."
println(str[0..14])
  1. 这使用操作符修饰符将中缀函数从上面的步骤进一步提取出来。
  2. times() 的操作符符号是 *,因此可以使用 2 * "Bye " 调用函数。
  3. 运算符函数允许对字符串进行轻松的范围访问。
  4. get()运算符启用了括号访问语法。

拓展函数和属性

Kotlin 允许您使用扩展机制向任何类添加新成员。也就是说,有两种类型的扩展: 扩展函数和扩展属性。它们看起来很像普通函数和属性,但有一个重要的区别: 您需要指定所扩展的类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
data class Item(val name: String, val price: Float)                                         // 1

data class Order(val items: Collection<Item>)

fun Order.maxPricedItemValue(): Float = this.items.maxByOrNull { it.price }?.price ?: 0F // 2
fun Order.maxPricedItemName() = this.items.maxByOrNull { it.price }?.name ?: "NO_PRODUCTS"

val Order.commaDelimitedItemNames: String // 3
get() = items.map { it.name }.joinToString()

fun main() {
val order = Order(listOf(Item("Bread", 25.0F), Item("Wine", 29.0F), Item("Water", 12.0F)))

println("Max priced item name: ${order.maxPricedItemName()}") // 4
println("Max priced item value: ${order.maxPricedItemValue()}")
println("Items: ${order.commaDelimitedItemNames}") // 5

}
  1. 定义 Item 和 Order 的简单模型。 Order 可以包含 Item 对象的集合。
  2. 为 Order 类型添加扩展函数。
  3. 为 Order 类型添加扩展属性。
  4. 直接对 Order 实例调用扩展函数。
  5. 访问 Order 实例的扩展属性。