Kotlin作为一门函数式编程言语,强调函数的重要性,高阶函数和Lambda表达式则是Kotlin中的独门武器。本文旨在介绍Kotlin高阶函数、Lambda表达式的语法,以及它们在函数式编程中的运用,协助读者更好地了解和运用函数式编程思维。

高阶函数

界说:

高阶函数是指能够接纳其他函数作为参数,或许回来一个函数的函数。在许多编程言语中,高阶函数都是一种重要的编程方法,因为它们供给了更高的笼统层次,使得代码愈加模块化、易于了解和保护。

下面是一个简单的比如,展现了如何在 Kotlin 中界说和运用高阶函数:

fun doOperation(x: Int, y: Int, operation: (Int, Int) -> Int): Int {
    return operation(x, y)
}
val result = doOperation(10, 5) { x, y -> x + y }
println(result) // 输出 15

界说一个 doOperation 函数,它承受两个整数 xy,以及一个函数 operation。这个函数承受两个整数参数,并回来一个整数。咱们能够将一个 Lambda 表达式作为 operation 参数传递给 doOperation 函数。

在这个比如中,将一个 Lambda 表达式 { x, y -> x + y } 传递给了 doOperation 函数,这个 Lambda 表达式表明将两个整数相加的操作。最终,doOperation 函数回来了相加后的成果 15,并打印了这个成果。

除了像上面比如中展现的那样,运用 Lambda 表达式作为函数参数外,Kotlin 还支持运用函数引证(function reference)来传递函数。例如,咱们能够运用 :: 运算符引证一个已经界说好的函数,然后将它作为高阶函数的参数传递。例如:

fun add(x: Int, y: Int) = x + y
val result = doOperation(10, 5, ::add)
println(result) // 输出 15

界说了一个名为 add 的函数,它承受两个整数参数,并回来它们的和。运用 :: 运算符引证这个函数,然后将它作为 doOperation 函数的参数传递。doOperation 函数将调用 add 函数,并回来相加后的成果 15

优势

  1. 代码重用

高阶函数能够接纳其他函数作为参数,这样能够让代码变得愈加灵敏和可重用。例如,咱们能够编写一个通用的排序函数,让它接纳一个比较函数作为参数,这样就能够在不同的场景中运用它进行排序,而不需要重新编写排序算法的中心部分。

  1. 更好的笼统

高阶函数能够协助咱们更好地笼统出程序的中心逻辑,然后让代码愈加清晰和易于了解。例如,咱们能够运用高阶函数来界说一些常用的操作,比如对列表进行过滤、映射等操作,这样能够让代码愈加简练和可读。

  1. 能够防止代码重复

高阶函数能够让咱们防止在代码中出现重复的逻辑。例如,咱们能够将一些常用的代码块封装在一个高阶函数中,然后在需要的当地调用它。这样能够减少代码冗余,进步代码的可保护性。

  1. 愈加灵敏

高阶函数能够让咱们编写愈加灵敏的代码。经过运用高阶函数,咱们能够将不同的功用组合在一起,然后完成愈加杂乱的操作。例如,咱们能够将一个函数作为另一个函数的参数,这样就能够完成函数的组合和嵌套。

常见的高阶函数

  1. map 函数:将调集中的每个元素都运用一个函数,并回来一个新的调集。
scssCopy code
val numbers = listOf(1, 2, 3, 4, 5)
val squaredNumbers = numbers.map { it * it }
println(squaredNumbers) // 输出 [1, 4, 9, 16, 25]
  1. filter 函数:回来一个新的调集,其间包括满意给定条件的一切元素。
scssCopy code
val numbers = listOf(1, 2, 3, 4, 5)
val evenNumbers = numbers.filter { it % 2 == 0 }
println(evenNumbers) // 输出 [2, 4]
  1. reduce 函数:将调集中的一切元素合并成一个值,详细的合并方法由指定的函数决议。
scssCopy code
val numbers = listOf(1, 2, 3, 4, 5)
val sum = numbers.reduce { acc, i -> acc + i }
println(sum) // 输出 15
  1. fold 函数:与 reduce 函数类似,可是能够指定一个初始值。
scssCopy code
val numbers = listOf(1, 2, 3, 4, 5)
val sum = numbers.fold(0) { acc, i -> acc + i }
println(sum) // 输出 15
  1. forEach 函数:对调集中的每个元素履行指定的操作,没有回来值。
scssCopy code
val numbers = listOf(1, 2, 3, 4, 5)
numbers.forEach { println(it) } // 顺次输出 1、2、3、4、5
  1. groupBy 函数:依据指定的键将调集分组,回来一个 Map 目标。
cssCopy code
data class Person(val name: String, val age: Int)
val people = listOf(
    Person("Alice", 20),
    Person("Bob", 22),
    Person("Charlie", 20),
    Person("David", 25)
)
val groupedPeople = people.groupBy { it.age }
println(groupedPeople) // 输出 {20=[Person(name=Alice, age=20), Person(name=Charlie, age=20)], 22=[Person(name=Bob, age=22)], 25=[Person(name=David, age=25)]}
  1. flatMap 函数:对调集中的每个元素运用一个函数,并将成果合并成一个新的调集。
javaCopy code
val words = listOf("hello", "world", "kotlin")
val chars = words.flatMap { it.toList() }
println(chars) // 输出 [h, e, l, l, o, w, o, r, l, d, k, o, t, l, i, n]
  1. sortedBy 函数:按照指定的排序规则对调集进行排序,并回来一个新的调集。
kotlinCopy code
data class Person(val name: String, val age: Int)
val people = listOf(
    Person("Alice", 20),
    Person("Bob", 22),
    Person("Charlie", 20),
    Person("David", 25)
)
val sortedPeople = people.sortedBy { it.age }
println(sortedPeople) // 输出 [Person(name=Alice, age=20), Person(name=Charlie, age=20), Person(name=Bob, age=22), Person(name=David, age=25)]
  1. takeWhile 函数:回来调集中从开端方位开端的连续元素,直到遇到榜首个不满意给定条件的元素。
scssCopy code
val numbers = listOf(1, 2, 3, 4, 5)
val result = numbers.takeWhile { it < 3 }
println(result) // 输出 [1, 2]
  1. any 函数:判别调集中是否存在满意给定条件的元素,回来一个布尔值。
scssCopy code
val numbers = listOf(1, 2, 3, 4, 5)
val result = numbers.any { it % 2 == 0 }
println(result) // 输出 true
  1. let

let 函数接纳一个lambda表达式,其回来值为lambda表达式中最终一行的成果,let 函数的参数则被传递到lambda表达式中。该函数主要用于防止空指针反常,简化对可空目标的判空操作。

kotlinCopy code
val str: String? = "Hello World"
str?.let { 
    // str不为null的情况下才会履行下面的句子
    println(it.length) // 输出 11
}
  1. also

also 函数和 let 函数很像,可是它回来的是调用目标本身,而不是最终一行的成果。also 函数的参数相同会传递到lambda表达式中。

kotlinCopy code
val list = mutableListOf<Int>()
list.also {
    // 对list进行一些操作,回来的是list本身
    it.add(1)
    it.add(2)
}.also {
    // 进一步操作list,回来的仍是list本身
    it.add(3)
}
println(list) // 输出 [1, 2, 3]
  1. with

with 函数不是扩展函数,而是一个独立函数,它接纳两个参数:一个目标和一个lambda表达式。lambda表达式中的代码能够直接拜访该目标的特点和方法,然后省去了在每个句子中重复引证目标的费事。

kotlinCopy code
data class Person(val name: String, var age: Int)
val person = Person("Tom", 20)
with(person) {
    age += 1
    println("My name is $name and I'm $age years old.")
} // 输出 "My name is Tom and I'm 21 years old."
  1. run

run 函数和 let 函数十分类似,它也是接纳一个lambda表达式,回来的是lambda表达式中最终一行的成果,但它和 let 的差异在于它的调用目标能够经过 this 关键字拜访,而不是经过lambda表达式的参数。

kotlinCopy code
val str: String = "Hello World"
val result = str.run {
    // 这里能够经过 this 拜访 str 目标
    println(this) // 输出 "Hello World"
    length
}
println(result) // 输出 11
  1. apply

apply 函数和 also 函数也很类似,它相同回来调用目标本身,可是它不承受lambda表达式的参数,而是在lambda表达式中直接操作调用目标。

data class Person(var name: String, var age: Int)
val person = Person("Tom", 20)
person.apply {
    name = "Jerry"
    age = 21
}
println(person.name) // 输出 "Jerry"
println(person.age) // 输出 21

最常运用的五个高阶函数的运用条件

函数式编程的魔法武器:Kotlin高阶函数和Lambda表达式

不得不知道的东西

kotlin 中说到高阶函数总是和以下几个概念或许用法一起出现,当然,你能够不知道这些用法或许概念,因为你能够不知道所以不用,可是你要阅读他人的代码,不能保证他人不用,这也是一些不愿意学习kotlin代码的老兵常说的可读性差,嗯,是可读性差吗?我感觉是X差。

都有什么呢?

  1. Lambda 表达式的语法:介绍 Lambda 表达式的语法,包括 Lambda 表达式的参数、函数体和回来值。
  2. 函数引证的运用:介绍函数引证的运用方法,包括运用 :: 运算符引证已经界说好的函数,并将其作为高阶函数的参数传递。
  3. Kotlin 中常用的函数式编程操作:介绍 Kotlin 中常用的函数式编程操作,包括:映射、过滤、折叠、遍历、分组、扁平化等。
  4. Kotlin 中其他函数式编程相关的特性:介绍 Kotlin 中其他函数式编程相关的特性,包括:部分函数、嵌套函数、尾递归函数、函数类型、函数类型参数等。
  5. Kotlin 中高阶函数的运用场景:介绍 Kotlin 中高阶函数的运用场景,包括:函数式编程、事情驱动编程、依靠注入、反射等。

其实许多都是概念性的东西,没有看过相关概念的时分你已经在运用了,今日就来归类一下你运用的东西是什么。

高阶函数总是和Lambda

Lambda 表达式是 Kotlin 中完成高阶函数的一种方法。高阶函数是指能够接纳其他函数作为参数或将函数作为回来值的函数。而 Lambda 表达式则是一种能够被传递和操作的匿名函数。

在 Kotlin 中,Lambda 表达式能够直接作为高阶函数的参数,这使得高阶函数的调用愈加简练和灵敏。运用 Lambda 表达式,咱们能够防止在调用高阶函数时重复界说一个新的函数。

简单介绍一下kotlin中的Lambda,

在 Kotlin 中,Lambda 表达式的语法是运用花括号 {} 包裹参数列表和代码块,中间用 -> 衔接。详细语法如下:

{ 参数列表 -> 代码块 }

其间,参数列表能够为空,也能够包括一个或多个参数,每个参数都包括一个参数名和类型,用冒号 : 分隔。代码块能够包括一条或多条句子,如果只要一条句子,则能够省掉花括号。Lambda 表达式的回来值类型能够主动推导出来,也能够运用 return 关键字显式指定回来值。

Kotlin Lambda 表达式的示例:

  1. 不带参数的 Lambda 表达式
val sayHello = { println("Hello, world!") }
sayHello()
  1. 带一个参数的 Lambda 表达式
val double = { n: Int -> n * 2 }
println(double(5))
  1. 带多个参数的 Lambda 表达式
val sum = { a: Int, b: Int -> a + b }
println(sum(3, 5))
  1. 带回来值的 Lambda 表达式
val greater = { a: Int, b: Int -> if (a > b) a else b }
println(greater(3, 5))
  1. 带类型推导的 Lambda 表达式
val list = listOf(1, 2, 3, 4, 5)
val evenList = list.filter { it % 2 == 0 }
println(evenList)

写时略微留意的当地

  1. 如果 Lambda 表达式的参数列表只要一个参数,能够省掉参数名,并用 it 代替参数名。例如:

    val list = listOf(1, 2, 3)
    list.forEach { println(it) }
    
  2. 如果 Lambda 表达式的参数列表为空,能够用空括号表明。例如:

    val action = { println("Hello") }
    
  3. 如果 Lambda 表达式的主体只要一条句子,能够省掉花括号。例如:

    val sum = { x: Int, y: Int -> x + y }
    
  4. Lambda 表达式能够有多个句子,这时需要用花括号包裹起来,并用 return 关键字回来成果。例如:

    val max = { x: Int, y: Int ->
        val temp = if (x > y) x else y
        temp
    }
    
  5. Lambda 表达式能够界说在函数外部,并且能够被命名和赋值。例如:

    val myLambda: (Int) -> Int = { x -> x * x }
    

函数引证的运用

函数引证是指将一个函数作为参数传递或许将其赋值给一个变量或许特点的方法。函数引证能够使代码愈加简练、易于阅读和保护。

常见的引证方法

  1. 引证顶层函数:
fun main() {
    val numbers = listOf(1, 2, 3, 4, 5)
    val evenNumbers = numbers.filter(::isEven)
    println(evenNumbers)
}
fun isEven(number: Int) = number % 2 == 0

运用 :: 操作符引证了 isEven 函数,并将其传递给了 filter 函数。这样做能够使代码愈加简练,不需要界说一个 lambda 表达式来过滤偶数。

  1. 引证目标的成员函数:
data class Person(val name: String, val age: Int)
fun main() {
    val people = listOf(Person("Alice", 29), Person("Bob", 31))
    val names = people.map(Person::name)
    println(names)
}

运用 :: 操作符引证了 Person 类的 name 特点,并将其传递给了 map 函数。这样做能够使代码愈加简练,不需要界说一个 lambda 表达式来提取人名。

  1. 引证结构函数:
data class Person(val name: String, val age: Int)
fun main() {
    val createPerson = ::Person
    val person = createPerson("Alice", 29)
    println(person)
}

运用 :: 操作符引证了 Person 类的结构函数,并将其赋值给了一个变量。然后咱们运用这个变量来创立一个 Person 目标。

Kotlin 中常用的函数式编程操作

Kotlin 作为一门支持函数式编程风格的编程言语,供给了许多常用的函数式编程操作,高阶函数便是其间之一,还有哪些呢?这么问谁都会懵逼,可是你看下,

  • Lambda 表达式 有用过吗
  • map\fold\filter等等有用过吗

其实都是大家运用过的,接下来看一下几个特有名词。

  1. 映射 (map)

映射操作能够将一个调集中的每个元素都运用一个函数,并将成果存储到一个新的调集中。在 Kotlin 中,能够运用 map() 函数来完成这个操作。

val numbers = listOf(1, 2, 3, 4, 5)
val squaredNumbers = numbers.map { it * it }
println(squaredNumbers) // [1, 4, 9, 16, 25]
  1. 过滤 (filter)

过滤操作能够依据一个条件从一个调集中挑选元素,并将成果存储到一个新的调集中。在 Kotlin 中,能够运用 filter() 函数来完成这个操作。

val numbers = listOf(1, 2, 3, 4, 5)
val evenNumbers = numbers.filter { it % 2 == 0 }
println(evenNumbers) // [2, 4]
  1. 折叠 (reduce)

折叠操作能够将一个调集中的元素聚组成一个值。在 Kotlin 中,能够运用 reduce() 函数来完成这个操作。

val numbers = listOf(1, 2, 3, 4, 5)
val sum = numbers.reduce { acc, n -> acc + n }
println(sum) // 15
  1. 遍历 (forEach)

遍历操作能够对一个调集中的每个元素都运用一个函数。在 Kotlin 中,能够运用 forEach() 函数来完成这个操作。

kotlinCopy code
val numbers = listOf(1, 2, 3, 4, 5)
numbers.forEach { println(it) }
  1. 分组 (groupBy)

分组操作能够依据一个特点将一个调集中的元素分组,并将成果存储到一个 Map 中。在 Kotlin 中,能够运用 groupBy() 函数来完成这个操作。

data class Person(val name: String, val age: Int)
val people = listOf(
    Person("Alice", 20),
    Person("Bob", 25),
    Person("Charlie", 20),
    Person("David", 30)
)
val peopleByAge = people.groupBy { it.age }
println(peopleByAge) // {20=[Person(name=Alice, age=20), Person(name=Charlie, age=20)], 25=[Person(name=Bob, age=25)], 30=[Person(name=David, age=30)]}
  1. 扁平化 (flatten)

扁平化操作能够将一个调集中的嵌套调集的元素展开,并将成果存储到一个新的调集中。在 Kotlin 中,能够运用 flatten() 函数来完成这个操作。

val nestedNumbers = listOf(listOf(1, 2), listOf(3, 4), listOf(5, 6))
val flattenedNumbers = nestedNumbers.flatten()
println(flattenedNumbers) // [1, 2, 3, 4, 5, 6]

哈哈,你可能会说了,你个糟老头子坏的很,这不是我都用过吗?

榜首:我不是糟老头子

第二:名词知道的越多,面试逼格就越高,而逼格的凹凸直接决议你money的多少

Kotlin 中其他函数式编程相关的特性

部分函数、嵌套函数、尾递归函数、函数类型、函数类型参数等,其间尾递归函数你能够插个眼,等我后边专门做一个分享。

  1. 部分函数(Local Function)是在函数内部界说的函数,只能在该函数内部调用。部分函数的作用是将杂乱的函数拆分成多个小函数,使代码更易于了解和保护。
fun main() {
    fun printMessage(message: String) {
        println(message)
    }
    printMessage("Hello World")
}
  1. 嵌套函数(Nested Function)是在一个函数内部界说的函数,与部分函数不同的是,嵌套函数能够在其外层函数和其他嵌套函数中调用。
fun main() {
    fun printMessage(message: String) {
        fun addExclamationMark(message: String): String {
            return "$message!"
        }
        println(addExclamationMark(message))
    }
    printMessage("Hello World")
}
  1. 尾递归函数(Tail Recursive Function)是指在函数的最终一步调用本身,并且不再进行任何操作。这种函数能够经过编译器的优化来防止栈溢出。
tailrec fun factorial(n: Int, acc: Int = 1): Int {
    return if (n == 0) acc else factorial(n - 1, acc * n)
}
  1. 函数类型(Function Type)是指将函数作为一种类型来运用。函数类型由参数类型和回来值类型组成,能够在变量声明、函数参数、回来值等当地运用。
val sum: (Int, Int) -> Int = { a, b -> a + b }
  1. 函数类型参数(Function Type Parameter)是指将函数类型作为另一个函数的参数。函数类型参数能够运用lambda表达式或许函数引证来进行传递。
fun <T, R> List<T>.map(transform: (T) -> R): List<R> {
    val result = mutableListOf<R>()
    for (item in this) {
        result.add(transform(item))
    }
    return result
}
val numbers = listOf(1, 2, 3, 4, 5)
val doubledNumbers = numbers.map { it * 2 }

Kotlin 中高阶函数的运用场景

  1. 函数式编程(Functional Programming):函数式编程的中心思维是将函数看作是一等公民,能够像其他数据类型相同传递、赋值和回来。高阶函数在函数式编程中被广泛运用,例如 map、filter、reduce 等常用的函数,它们能够将函数作为参数进行传递,完成对调集等数据结构的操作。
  2. 事情驱动编程(Event-driven Programming):事情驱动编程是一种基于事情和回调函数的编程形式。高阶函数能够作为回调函数,在事情触发时被调用。例如在 Android 中,OnClickListener 便是一个高阶函数,用于处理 View 的点击事情。
  3. 依靠注入(Dependency Injection):依靠注入是一种经过将依靠联系从一个目标传递给另一个目标来完成松耦合的编程技术。高阶函数能够用于依靠注入中的回调函数,例如在 Android 中,经过调用 setOnClickListener 方法,将一个函数作为参数传递给 View,完成对 View 的点击事情的处理。
  4. 反射(Reflection):反射是一种在运行时动态获取和操作目标的技术。Kotlin 中的高阶函数能够与反射结合运用,例如经过调用 KClass 的函数 findFunction,能够在运行时获取函数的引证,从而调用该函数。

总结

经过本文,咱们深入探讨了Kotlin中高阶函数和Lambda表达式的语法和运用,以及它们在函数式编程中的重要性和运用场景。咱们学习了一些常用的函数式编程操作,如map、filter、reduce等,了解了函数式编程的特性。