Kotlin基础(十):函数进阶

news2024/11/17 9:28:56

前言

本文主要讲解kotlin函数,之前系列文章中提到过函数,本文是kotlin函数的进阶内容。


Kotlin文章列表

Kotlin文章列表: 点击此处跳转查看


目录

在这里插入图片描述


1.1 函数基本用法

Kotlin 是一种现代的静态类型编程语言,它在函数的定义和使用上有一些特点。以下是 Kotlin 函数的基本用法:

  1. 函数的定义:
    在 Kotlin 中,可以使用关键字 fun 来定义函数。函数定义的一般语法如下:

    fun functionName(parameters): returnType {
        // 函数体
    }
    
    • functionName 是函数的名称,根据命名规范使用驼峰命名法。
    • parameters 是函数的参数列表,可以包含零个或多个参数。每个参数由参数名和参数类型组成,用逗号分隔。
    • returnType 是函数的返回类型,指定函数执行后返回的数据类型。如果函数不返回任何值,可以使用 Unit 类型或省略返回类型。
  2. 函数的调用:
    定义函数后,可以通过函数名称和传递的参数来调用函数。调用函数时,传递的参数必须与函数定义中的参数类型和顺序相匹配。

    val result = functionName(argument1, argument2, ...)
    
  3. 函数的返回值:
    函数可以有返回值,返回值的类型在函数定义中指定。使用 return 关键字将结果返回给调用者。

    fun addNumbers(a: Int, b: Int): Int {
        return a + b
    }
    

    在上面的示例中,addNumbers 函数接收两个整数类型的参数并返回它们的和。

  4. 默认参数:
    Kotlin 允许在函数定义中为参数设置默认值。这意味着在调用函数时可以省略具有默认值的参数。

    fun greet(name: String = "Guest") {
        println("Hello, $name!")
    }
    

    在上述示例中,greet 函数有一个默认参数 name,如果调用时不提供参数,将使用默认值 “Guest”。

  5. 可变数量的参数:
    Kotlin 支持可变数量的参数,也称为可变参数。在函数定义中,可以指定最后一个参数为可变参数,它可以接收零个或多个值。

    fun printNumbers(vararg numbers: Int) {
        for (number in numbers) {
            println(number)
        }
    }
    

    在上述示例中,printNumbers 函数接收可变数量的整数参数,并在循环中逐个打印出来。

这些是 Kotlin 函数的基本用法。除此之外,Kotlin 还支持高阶函数、扩展函数、Lambda 表达式等功能,可以进一步扩展函数的灵活性和表达能力。


1.2 使用中缀标记法调用函数

在 Kotlin 中,你可以使用中缀标记法(Infix Notation)来调用特定的函数。中缀标记法允许我们省略点和括号,并且可以更加简洁地调用某些特定类型的函数。为了能够使用中缀标记法,需要满足以下条件:

  1. 这个函数必须是一个成员函数或扩展函数。
  2. 这个函数必须只有一个参数。
  3. 这个函数必须使用 infix 修饰符进行标记。

使用中缀标记法调用函数的语法如下:

infix fun ReceiverType.functionName(parameter: ParameterType): ReturnType {
    // 函数体
}

其中:

  • ReceiverType: 函数的接收者类型,可以是类的类型或者扩展函数的接收者类型。
  • functionName: 函数的名称。
  • ParameterType: 函数的参数类型。
  • ReturnType: 函数的返回类型。
  • 函数体: 函数的实际逻辑操作。

在中缀标记法中,可以省略点和括号,并且可以更加直观地表达函数调用。以下是一个示例:

class Person(val name: String) {
    infix fun says(message: String) {
        println("$name says: $message")
    }
}

fun main() {
    val person = Person("Alice")
    person says "Hello, Kotlin!" // 使用中缀标记法调用函数
}

在上面的例子中,我们定义了一个 Person 类,其中有一个 says 函数。由于这个函数使用了 infix 修饰符,并且只有一个参数,我们可以在调用时省略点和括号,直接使用中缀标记法来调用该函数。

输出结果为:

Alice says: Hello, Kotlin!

中缀标记法通常用于表示某种关联或操作,使得代码更加简洁易读。但要注意,不是所有的函数都适合使用中缀标记法,应该根据场景和语义来判断是否使用该特性。


1.3 单表达式函数

在 Kotlin 中,你可以使用可变参数(Variable Number of Arguments)来允许函数接受不定数量的参数。可变参数使得函数能够接受任意数量的相同类型的参数,而无需显式地定义一个数组或列表来传递参数。在函数定义中使用 vararg 关键字即可实现可变参数。

可变参数的语法如下:

fun functionName(vararg parameterName: Type): ReturnType {
    // 函数体
}

其中:

  • functionName: 函数的名称。
  • parameterName: 可变参数的名称,它被声明为 vararg 类型,后面跟着参数类型 Type
  • ReturnType: 函数的返回类型。
  • 函数体: 可变参数在函数体内可以当作数组来使用。

下面是一个简单的例子,演示了如何使用可变参数来计算一组数字的总和:

fun sum(vararg numbers: Int): Int {
    var total = 0
    for (number in numbers) {
        total += number
    }
    return total
}

你可以使用这个函数来传递任意数量的整数参数,它们将会被作为数组在函数体内处理,并返回它们的总和。例如:

val result1 = sum(1, 2, 3) // 结果为 6
val result2 = sum(10, 20, 30, 40, 50) // 结果为 150
val result3 = sum(5) // 结果为 5

需要注意的是,可变参数只能在函数参数列表的末尾使用,并且在同一个函数中只能有一个可变参数。如果函数需要接受不同类型的参数或多个可变参数,可以使用命名参数或重载函数的方式来处理。


1.4 函数参数和返回值

1.4.1 可变参数

在 Kotlin 中,可变参数(Variable Number of Arguments)允许函数接受不定数量的相同类型参数。可变参数使得函数能够接受任意数量的参数,而无需事先指定参数的数量。在函数定义中使用 vararg 关键字即可实现可变参数。

可变参数的语法如下:

fun functionName(vararg parameterName: Type): ReturnType {
    // 函数体
}

其中:

  • functionName: 函数的名称。
  • parameterName: 可变参数的名称,它被声明为 vararg 类型,后面跟着参数类型 Type
  • ReturnType: 函数的返回类型。
  • 函数体: 可变参数在函数体内可以当作数组来使用。

下面是一个简单的例子,演示了如何使用可变参数来计算一组数字的总和:

fun sum(vararg numbers: Int): Int {
    var total = 0
    for (number in numbers) {
        total += number
    }
    return total
}

你可以使用这个函数来传递任意数量的整数参数,它们将会被作为数组在函数体内处理,并返回它们的总和。例如:

val result1 = sum(1, 2, 3) // 结果为 6
val result2 = sum(10, 20, 30, 40, 50) // 结果为 150
val result3 = sum(5) // 结果为 5

需要注意的是,可变参数只能在函数参数列表的末尾使用,并且在同一个函数中只能有一个可变参数。如果函数需要接受不同类型的参数或多个可变参数,可以使用命名参数或重载函数的方式来处理。


1.4.2 返回值类型

在 Kotlin 中,函数的返回值类型用于指定函数在执行完毕后将会返回的值的类型。在函数定义中,通过在函数名称后使用冒号(:)来声明返回值类型。返回值类型可以是 Kotlin 中的任意类型,包括基本数据类型(如 Int、Boolean、Double 等)和自定义的类类型。

函数的返回值类型的语法如下:

fun functionName(parameters): ReturnType {
    // 函数体
    return value // 返回值必须与 ReturnType 类型相符
}

其中:

  • functionName: 函数的名称。
  • parameters: 函数的参数列表,如果没有参数则可以省略。
  • ReturnType: 函数的返回值类型。
  • 函数体: 函数的实际逻辑操作。
  • return value: 使用 return 关键字来返回一个与 ReturnType 类型相符的值。

以下是一些返回值类型的例子:

fun add(a: Int, b: Int): Int {
    return a + b
}

fun isPositive(number: Int): Boolean {
    return number > 0
}

fun greet(name: String): String {
    return "Hello, $name!"
}

在上面的例子中:

  • add 函数接收两个整数参数 ab,返回它们的和,因此返回值类型是 Int
  • isPositive 函数接收一个整数参数 number,判断该数字是否为正数,返回布尔值,因此返回值类型是 Boolean
  • greet 函数接收一个字符串参数 name,返回一个拼接了问候语的字符串,因此返回值类型是 String

如果函数体只有一个表达式,还可以使用单表达式函数的简写方式,如下所示:

fun add(a: Int, b: Int) = a + b
fun isPositive(number: Int) = number > 0
fun greet(name: String) = "Hello, $name!"

在这种情况下,返回值类型会被自动推断出来,无需显式声明。


1.5 函数的范围

1.5.1 局部函数

在Kotlin中,局部函数是指在函数内部定义的函数。它们的作用范围仅限于包含它们的函数内部,无法从外部进行访问。局部函数通常用于将一些具体功能划分为更小的逻辑单元,提高代码的可读性和可维护性。

局部函数在生活中的一个例子是,在一个购物应用中,有一个函数用于计算订单的总价格。而在这个函数内部,你可能会定义一个局部函数来计算折扣金额,以便更好地组织代码。

下面是使用局部函数的步骤和示例代码:

fun calculateTotalPrice(items: List<Item>, discount: Double): Double {
    fun calculateDiscountedPrice(price: Double): Double {
        // 局部函数用于计算折扣后的价格
        return price * (1 - discount)
    }

    var totalPrice = 0.0
    for (item in items) {
        totalPrice += calculateDiscountedPrice(item.price)
    }
    
    return totalPrice
}

在上面的示例中,calculateTotalPrice函数接受一个List类型的items参数和一个discount参数,它首先定义了一个局部函数calculateDiscountedPrice来计算折扣后的价格。然后,它遍历items列表,并将每个物品的折扣后价格累加到totalPrice变量中。最后,返回总价格。

这个例子中,局部函数calculateDiscountedPrice只在calculateTotalPrice函数内部可见,无法从外部进行访问。这种封装性使得代码更加清晰和可维护。

局部函数的底层原理是,它们在编译时被编译成包含外部函数的内部类或者静态方法。这样可以确保局部函数的作用范围仅限于外部函数内部。

局部函数的优点包括:

  1. 提高代码的可读性和可维护性:通过将复杂逻辑拆分为更小的逻辑单元,代码更易于理解和调试。
  2. 封装性:局部函数只在外部函数内部可见,可以避免污染外部命名空间,提高代码的安全性。

使用局部函数时需要注意以下几点:

  1. 局部函数只能在定义它们的函数内部调用,无法从外部访问。
  2. 局部函数不能被声明为顶层函数或类的成员函数。

局部函数适用于需要在一个函数内部封装一些具体功能的情况,尤其是这些功能只在该函数内部使用,并且不需要从外部进行访问。例如,计算总价、处理某个特定功能的逻辑等。


1.5.2 成员函数

在Kotlin中,成员函数是指定义在类或对象内部的函数。它们是与类或对象关联的函数,可以通过类或对象进行调用。成员函数用于封装特定功能,并可以访问类或对象的属性和其他成员。

成员函数在生活中的一个例子是,在一个学生类中,有一个函数用于计算学生的平均成绩。这个函数是类的一部分,它可以访问学生对象的成绩属性并进行计算。

下面是使用成员函数的步骤和示例代码:

class Student(val name: String) {
    private val scores = mutableListOf<Int>() // 学生成绩列表

    fun addScore(score: Int) {
        scores.add(score)
    }

    fun calculateAverageScore(): Double {
        var sum = 0
        for (score in scores) {
            sum += score
        }
        return sum.toDouble() / scores.size
    }
}

fun main() {
    val student = Student("Alice")
    student.addScore(90)
    student.addScore(85)
    student.addScore(95)
    val averageScore = student.calculateAverageScore()
    println("Average score of ${student.name}: $averageScore")
}

在上面的示例中,我们定义了一个Student类,它包含一个成员函数addScore用于向学生成绩列表中添加分数,并且还定义了一个成员函数calculateAverageScore用于计算平均分。在main函数中,我们创建了一个名为student的学生对象,添加了一些分数,并通过调用calculateAverageScore函数来计算平均分。

成员函数的底层原理是,它们被编译成与类或对象关联的普通函数,并且可以访问类或对象的成员变量和其他成员函数。

成员函数的优点包括:

  1. 封装性:成员函数可以访问类或对象的属性和其他成员,实现了相关功能的封装。
  2. 代码组织:成员函数将相关的功能组织在一起,提高了代码的可读性和可维护性。

使用成员函数时需要注意以下几点:

  1. 成员函数必须在类或对象内部进行定义。
  2. 成员函数可以访问类或对象的属性和其他成员。

成员函数适用于需要在类或对象内部封装特定功能的情况,特别是这些功能与类或对象的状态和属性密切相关。


1.6 泛型函数

1.6.1 泛型函数简介

在Kotlin中,泛型函数是指能够处理多种类型的函数。它们使用类型参数来表示可以灵活指定的类型,以便在函数内部进行通用的操作。泛型函数的作用是提高代码的重用性和灵活性。

下面是使用泛型函数的步骤和示例代码:

class Contact(val name: String, val phone: String)

fun <T> findContactByName(contacts: List<T>, name: String): T? {
    for (contact in contacts) {
        if (contact is Contact && contact.name == name) {
            return contact
        }
    }
    return null
}

fun main() {
    val contacts = listOf(
        Contact("Alice", "123456"),
        Contact("Bob", "789012"),
        Contact("Charlie", "345678")
    )
    val contact = findContactByName(contacts, "Bob")
    if (contact != null) {
        println("Phone number of ${contact.name}: ${contact.phone}")
    } else {
        println("Contact not found.")
    }
}

在上面的示例中,我们定义了一个泛型函数findContactByName,它接受一个类型参数T和一个联系人列表contacts,以及一个要查找的姓名name。在函数内部,我们遍历联系人列表,并根据联系人的类型和姓名进行匹配查找。最后,返回匹配到的联系人或者null

main函数中,我们创建了一个联系人列表contacts,并通过调用findContactByName函数来查找姓名为"Bob"的联系人。如果找到了对应的联系人,则打印其姓名和电话号码;如果没有找到,则打印"Contact not found."。

泛型函数的底层原理是,在编译时会进行类型擦除,将泛型函数的代码生成为实际类型的函数。通过类型参数的擦除,可以在运行时处理不同类型的对象。

泛型函数的优点包括:

  1. 代码重用性:泛型函数可以适用于多种类型,提高了代码的重用性。
  2. 灵活性:泛型函数可以根据不同的类型进行通用操作,增加了代码的灵活性。

使用泛型函数时需要注意以下几点:

  1. 在泛型函数内部,对类型参数的操作受到一些限制,因为无法确定类型参数的具体类型。
  2. 需要在调用泛型函数时指定类型参数的具体类型。

泛型函数适用于需要在不同类型上执行相似操作的情况,特别是在代码中存在重复逻辑,并且需要处理不同类型的数据。


1.6.2 kotlin泛型函数在Android中使用

Kotlin 泛型函数在 Android 开发中非常常见,特别是在处理集合、列表、适配器等数据类型时。泛型函数能够让我们写出更通用和灵活的代码,使得在不同类型的数据上都能使用相同的逻辑。

以下是在 Android 中使用 Kotlin 泛型函数的一些常见场景:

  1. 在 RecyclerView 适配器中使用泛型函数:
class MyAdapter<T>(private val dataList: List<T>) : RecyclerView.Adapter<MyViewHolder<T>>() {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder<T> {
        // 创建 ViewHolder
    }

    override fun onBindViewHolder(holder: MyViewHolder<T>, position: Int) {
        // 绑定数据到 ViewHolder
        val item = dataList[position]
        holder.bind(item)
    }

    override fun getItemCount(): Int {
        return dataList.size
    }
}

class MyViewHolder<T>(private val itemView: View) : RecyclerView.ViewHolder(itemView) {
    fun bind(data: T) {
        // 绑定数据
    }
}

在这个例子中,我们定义了一个泛型适配器 MyAdapter,它能够适用于不同类型的数据列表。在 MyAdapter 中使用了泛型类型 T,它表示适配器可以接收任意类型的数据。同时,我们也定义了一个泛型 ViewHolder MyViewHolder,使得 ViewHolder 能够绑定不同类型的数据。

  1. 使用泛型函数处理集合数据:
fun <T> filterList(list: List<T>, predicate: (T) -> Boolean): List<T> {
    val filteredList = mutableListOf<T>()
    for (item in list) {
        if (predicate(item)) {
            filteredList.add(item)
        }
    }
    return filteredList
}

// 使用泛型函数来过滤整数列表中的偶数
val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val evenNumbers = filterList(numbers) { it % 2 == 0 }

在这个例子中,我们定义了一个泛型函数 filterList,它接收一个列表和一个谓词函数(predicate),然后返回满足谓词条件的新列表。通过这个泛型函数,我们可以方便地处理不同类型的数据列表,而无需写多个不同的过滤函数。

这只是在 Android 中使用 Kotlin 泛型函数的两个示例,实际上在 Android 开发中还有许多其他场景可以应用泛型函数。总的来说,泛型函数是一种非常有用的特性,它能够提高代码的灵活性和可重用性,使得我们能够更加优雅地处理不同类型的数据。


1.7 内联函数

在Kotlin中,内联函数是指在编译时将函数调用处的代码直接复制到调用处的位置,而不是通过函数调用的方式执行。内联函数的作用是减少函数调用的开销,提高代码的执行效率。

内联函数在生活中的一个例子是,在一个数学计算应用中,有一个函数用于计算两个数的平方和。这个函数可能会被频繁调用,如果每次调用都通过函数调用的方式执行,会带来一定的性能开销。而使用内联函数可以将函数体的代码直接复制到调用处,减少了函数调用的开销。

下面是使用内联函数的步骤和示例代码:

inline fun calculateSumOfSquares(a: Int, b: Int): Int {
    val squareA = a * a
    val squareB = b * b
    return squareA + squareB
}

fun main() {
    val result = calculateSumOfSquares(3, 4)
    println("Sum of squares: $result")
}

在上面的示例中,我们定义了一个内联函数calculateSumOfSquares,它接受两个整数参数ab,并计算它们的平方和。在函数调用处,编译器会将函数体的代码直接复制到调用处,而不是通过函数调用的方式执行。

main函数中,我们调用了calculateSumOfSquares函数,并将结果打印出来。

内联函数的底层原理是,在编译时将函数调用处的代码直接复制到调用处的位置,避免了函数调用的开销。这样可以减少函数调用的开销,并提高代码的执行效率。

内联函数的优点包括:

  1. 减少函数调用开销:内联函数将函数调用处的代码直接复制到调用处,避免了函数调用的开销,提高了代码的执行效率。
  2. 灵活性:内联函数可以处理函数参数中的 lambda 表达式,并在调用处进行优化。

使用内联函数时需要注意以下几点:

  1. 内联函数可能会增加生成的字节码大小,因为代码会被复制到调用处,可能导致编译后的字节码变大。
  2. 内联函数不适用于函数体过大的情况,因为过大的函数体会导致生成的字节码过大,反而会降低性能。

内联函数适用于需要频繁调用的函数,并且函数体较小的情况。它可以减少函数调用的开销,提高代码的执行效率。 ​


1.7.1 让Lambda表达式内联进函数

在Kotlin中,让Lambda表达式内联进函数是指在编译时将Lambda表达式的代码直接复制到函数调用处,而不是通过函数调用的方式执行Lambda表达式。这样可以减少Lambda表达式的函数调用开销,提高代码的执行效率。

下面是使用让Lambda表达式内联进函数的步骤和示例代码:

data class Student(val name: String, val score: Int)

inline fun filterPassingStudents(students: List<Student>, predicate: (Student) -> Boolean): List<Student> {
    val passingStudents = mutableListOf<Student>()
    for (student in students) {
        if (predicate(student)) {
            passingStudents.add(student)
        }
    }
    return passingStudents
}

fun main() {
    val students = listOf(
        Student("Alice", 80),
        Student("Bob", 90),
        Student("Charlie", 70)
    )
    val passingStudents = filterPassingStudents(students) { it.score >= 75 }
    println("Passing students: $passingStudents")
}

在上面的示例中,我们定义了一个让Lambda表达式内联进函数的函数filterPassingStudents,它接受一个学生列表students和一个Lambda表达式predicate,用于判断学生是否及格。在函数调用处,编译器会将Lambda表达式的代码直接复制到调用处,而不是通过函数调用的方式执行。

main函数中,我们创建了一个学生列表students,并通过调用filterPassingStudents函数来筛选出及格的学生。Lambda表达式{ it.score >= 75 }用于判断学生的分数是否大于等于75。最后,将筛选出的及格学生打印出来。

让Lambda表达式内联进函数的底层原理和优缺点与内联函数相似,即在编译时将Lambda表达式的代码直接复制到函数调用处,减少了函数调用的开销。优点包括提高代码执行效率,减少函数调用开销;缺点包括增加生成的字节码大小。使用时需要注意Lambda表达式的函数体大小,避免过大的函数体导致生成的字节码过大。

让Lambda表达式内联进函数适用于需要频繁调用的函数,并且Lambda表达式的函数体较小的情况。它可以减少Lambda表达式的函数调用开销,提高代码的执行效率。


1.7.2 内联部分Lambda表达式

在Kotlin中,内联部分Lambda表达式是指在函数调用时将Lambda表达式的一部分代码内联进函数,而将另一部分代码作为参数传递。这样可以减少部分Lambda表达式的函数调用开销,提高代码的执行效率。

下面是使用内联部分Lambda表达式的步骤和示例代码:

inline fun executeAsyncTask(crossinline task: () -> Unit, onComplete: () -> Unit) {
    // 执行异步任务
    // ...
    task() // 将任务的一部分代码内联进函数
    // ...
    onComplete() // 执行回调函数的另一部分代码
}

fun main() {
    val task = {
        // 异步任务的代码
        println("Executing async task...")
    }
    val onComplete = {
        // 回调函数的代码
        println("Async task completed.")
    }
    executeAsyncTask(task, onComplete)
}

在上面的示例中,我们定义了一个内联部分Lambda表达式的函数executeAsyncTask,它接受一个任务的Lambda表达式task和一个完成回调的Lambda表达式onComplete。在函数内部,我们首先执行异步任务的一部分代码(即将task内联进函数),然后执行回调函数的另一部分代码(即onComplete)。

main函数中,我们创建了一个任务的Lambda表达式task,其中包含异步任务的代码。我们还创建了一个完成回调的Lambda表达式onComplete,其中包含回调函数的代码。最后,我们调用executeAsyncTask函数,并将taskonComplete作为参数传递。

内联部分Lambda表达式的底层原理和优缺点与内联函数相似,即在编译时将Lambda表达式的一部分代码内联进函数。优点包括减少Lambda表达式的函数调用开销,提高代码执行效率;缺点包括增加生成的字节码大小。使用时需要注意Lambda表达式的函数体大小,避免过大的函数体导致生成的字节码过大。

内联部分Lambda表达式适用于需要频繁调用的函数,并且Lambda表达式的一部分代码较小的情况。它可以减少Lambda表达式的函数调用开销,提高代码的执行效率。


1.7.3 非局部返回(Non-localreturn)

在Kotlin中,内联函数非局部返回是指在内联函数中,可以使用return语句直接从内联函数外部返回,而不仅仅是从内联函数内部返回。这种特性允许在内联函数中进行非局部的控制流程操作,例如从调用内联函数的位置直接返回到调用者的位置。

下面是使用内联函数非局部返回的步骤和示例代码:

inline fun performOperation(numbers: List<Int>, operation: (Int) -> Boolean): Boolean {
    numbers.forEach {
        if (operation(it)) {
            return true // 使用非局部返回直接返回到调用者的位置
        }
    }
    return false
}

fun main() {
    val numbers = listOf(1, 2, 3, 4, 5)
    val result = performOperation(numbers) {
        if (it == 3) {
            return@performOperation true // 使用标签限定返回到调用者的位置
        }
        false
    }
    println("Result: $result")
}

在上面的示例中,我们定义了一个内联函数performOperation,它接受一个整数列表numbers和一个操作的Lambda表达式operation。在函数内部,我们使用forEach函数遍历列表中的每个元素,如果操作满足条件,则使用非局部返回直接返回到调用者的位置。

main函数中,我们创建了一个整数列表numbers,并调用performOperation函数来查找是否存在满足条件的元素。在Lambda表达式中,我们使用return@performOperation标签限定了非局部返回的位置。如果列表中存在值为3的元素,则返回true,否则返回false。最后,将结果打印出来。

内联函数非局部返回的底层原理是在编译时通过内联将非局部返回的位置信息嵌入到代码中,使得在满足条件时直接返回到调用者的位置。这样可以减少不必要的迭代操作,提高代码执行效率。

内联函数非局部返回的优点是可以简化代码结构,避免额外的迭代操作,并提高代码执行效率。然而,过度使用非局部返回可能导致代码的可读性和维护性降低,因此需要谨慎使用。

使用内联函数非局部返回时需要注意以下几点:

  1. 使用标签限定返回的位置:在Lambda表达式中使用标签限定非局部返回的位置,确保返回到调用者的正确位置。
  2. 慎重使用:过度使用非局部返回可能导致代码难以理解和维护,只在需要提高代码执行效率时使用。

内联函数非局部返回适用于需要在内联函数中进行非局部控制流程操作的场景,例如在迭代操作中查找满足条件的元素并立即返回。


1.7.4 实体化的类型参数(Reifiedtypeparameter)

在Kotlin中,内联函数实体化的类型参数(reified type parameter)是指在内联函数中声明的类型参数,可以在运行时访问和操作其实际类型信息。这种特性使得我们可以在内联函数中使用类型参数进行具体的运行时操作,而不仅仅局限于编译时的类型擦除。

内联函数实体化的类型参数例子:在一个数据转换的应用中,有一个内联函数用于将一个类型的数据转换为另一个类型的数据。使用内联函数实体化的类型参数可以在函数内部获取和操作转换前后的实际类型信息。

下面是使用内联函数实体化的类型参数的步骤和示例代码:

inline fun <reified T> convertData(data: Any): T? {
    if (data is T) {
        return data // 返回转换后的数据
    }
    return null
}

fun main() {
    val strData = "Hello, World!"
    val intData = 10

    val convertedStr = convertData<String>(strData)
    val convertedInt = convertData<Int>(intData)

    println("Converted String: $convertedStr")
    println("Converted Int: $convertedInt")
}

在上面的示例中,我们定义了一个内联函数convertData,它接受一个任意类型的数据data和一个实体化的类型参数T。在函数内部,我们使用is操作符检查data是否是类型T的实例,如果是,则返回转换后的数据,否则返回null

main函数中,我们分别将一个字符串类型的数据strData和一个整数类型的数据intData传递给convertData函数,并指定要转换的目标类型为StringInt。然后,将转换后的结果打印出来。

内联函数实体化的类型参数的底层原理是在编译时通过内联将类型参数的实际类型信息嵌入到代码中,使得在运行时可以访问和操作该类型信息。这样可以实现在内联函数中对类型参数进行具体的运行时操作。

内联函数实体化的类型参数的优点是可以在内联函数内部访问和操作类型参数的实际类型信息,提供了更灵活的运行时操作。缺点是会增加编译后的代码大小。

在使用内联函数实体化的类型参数时,需要注意以下几点:

  1. 使用关键字reified:在类型参数声明前使用reified关键字标记,表示要进行实体化的类型参数。
  2. 仅限于内联函数:内联函数实体化的类型参数只能用于内联函数中。
  3. 类型擦除的限制:内联函数实体化的类型参数只能获取和操作具体的非泛型类型信息,无法获取和操作泛型类型的实际类型信息。

内联函数实体化的类型参数适用于需要在内联函数中访问和操作类型参数的实际类型信息的场景,例如类型转换、类型检查等。


1.7.5 内联属性

在Kotlin中,内联函数的内联属性是指在内联函数中声明的属性,它们的访问器会在编译时直接嵌入到调用它们的地方,而不是通过函数调用的方式进行访问。这种特性可以提高代码的执行效率,减少函数调用的开销。

内联函数的内联属性的例子:在一个计算属性的应用中,有一个内联函数用于计算某个属性的值。使用内联属性可以直接在调用它的地方获取属性的值,而无需通过函数调用。

下面是使用内联函数的内联属性的步骤和示例代码:

inline class User(val name: String) {
    val formattedName: String
        get() = "Hello, $name" // 内联属性的访问器会在编译时嵌入到调用它的地方

    inline fun greet() {
        println(formattedName) // 直接访问内联属性,无需函数调用
    }
}

fun main() {
    val user = User("Alice")
    user.greet()
}

在上面的示例中,我们定义了一个内联类User,它有一个属性name和一个内联属性formattedName。在formattedName的访问器中,我们使用字符串模板将name格式化为一个问候语。在greet函数中,我们直接访问内联属性formattedName,无需通过函数调用。

main函数中,我们创建了一个User对象,并调用greet函数打印问候语。

内联函数的内联属性的底层原理是在编译时将属性的访问器嵌入到调用它的地方,而不是通过函数调用的方式进行访问。这样可以减少函数调用的开销,提高代码的执行效率。

内联函数的内联属性的优点是可以减少函数调用的开销,提高代码的执行效率。缺点是会增加编译后的代码大小。

在使用内联函数的内联属性时,需要注意以下几点:

  1. 内联属性只能用于内联函数中:内联属性只能在内联函数内部声明和使用。
  2. 注意编译后的代码大小:内联属性会增加编译后的代码大小,因此在使用时需要权衡代码的执行效率和代码大小的关系。

内联函数的内联属性适用于需要在内联函数中声明和使用属性,并希望减少函数调用开销的场景。


1.7.6 kotlin内联函数在Android中使用

Kotlin 内联函数在 Android 开发中是一种常见的优化技术。内联函数在编译时会将函数调用处的代码拷贝到调用处,避免了函数调用的开销,从而提高了性能。这在 Android 中尤其有用,因为 Android 应用通常需要频繁调用许多小函数,内联函数能够在一定程度上减少函数调用带来的性能损耗。

在 Android 开发中,我们可以使用内联函数来优化高阶函数(Higher-order Functions)和 Lambda 表达式的调用。高阶函数和 Lambda 表达式通常涉及匿名函数的创建和调用,而这些匿名函数在运行时会导致额外的开销,内联函数能够帮助我们避免这些开销。

以下是一个在 Android 中使用内联函数的示例:

inline fun measureTime(block: () -> Unit) {
    val startTime = System.currentTimeMillis()
    block()
    val endTime = System.currentTimeMillis()
    val executionTime = endTime - startTime
    Log.d("TAG", "Execution time: $executionTime milliseconds")
}

// 在 Android 中调用内联函数
fun someExpensiveOperation() {
    measureTime {
        // 这里是一些耗时的操作
        for (i in 1..1000000) {
            // 一些计算或其他操作
        }
    }
}

在这个例子中,我们定义了一个内联函数 measureTime,它接收一个无参数的 Lambda 表达式作为参数。在 measureTime 函数内部,我们记录了 Lambda 表达式的执行时间,并将其打印出来。

然后,在 someExpensiveOperation 函数中,我们调用了 measureTime 内联函数,并在 Lambda 表达式中执行一些耗时的操作。由于 measureTime 是内联函数,函数调用的代码将会被拷贝到调用处,这样就避免了函数调用的开销,从而减少了性能损耗。

另外一个案例:假设你有一个应用程序,其中有一个用于执行网络请求的类 NetworkClient。在这个类中,你可能有一个通用的方法来执行网络请求,如下所示:

class NetworkClient {
    fun <T> makeRequest(url: String, onSuccess: (response: T) -> Unit, onError: (error: Throwable) -> Unit) {
        // 在这里执行网络请求,然后调用回调函数
        // 成功时调用 onSuccess,传递响应数据
        // 失败时调用 onError,传递错误信息
    }
}

在上面的代码中,makeRequest 方法是一个高阶函数,它接收一个 url 参数,以及两个回调函数 onSuccessonError,用于处理网络请求成功和失败的情况。

如果你在应用程序的多个地方使用了这个网络请求方法,可能会导致在每次调用时都会创建匿名的 Lambda 表达式,从而产生额外的对象创建和函数调用的开销。为了优化性能,我们可以使用内联函数来避免这个问题。

首先,我们将 makeRequest 方法声明为内联函数:

inline fun <T> NetworkClient.makeRequest(url: String, crossinline onSuccess: (response: T) -> Unit, crossinline onError: (error: Throwable) -> Unit) {
    // 在这里执行网络请求,然后调用回调函数
    // 成功时调用 onSuccess,传递响应数据
    // 失败时调用 onError,传递错误信息
}

接着,我们使用 crossinline 修饰符来标记回调函数,以便在内联函数中使用它们。

现在,在应用程序中调用网络请求方法时,我们可以使用内联函数来优化性能:

val networkClient = NetworkClient()

networkClient.makeRequest("https://example.com/data",
    onSuccess = { response ->
        // 处理成功响应
    },
    onError = { error ->
        // 处理错误情况
    }
)

由于 makeRequest 方法是内联函数,Lambda 表达式中的代码将会被拷贝到调用处,避免了函数调用的开销。这样就提高了性能,同时保持了代码的简洁性和可读性。


1.8 高阶函数

1.8.1 高阶函数的简介

在 Kotlin 中,高阶函数(Higher-order Functions)是一种特殊的函数,它们可以接收一个或多个函数作为参数,也可以返回一个函数。高阶函数使得函数可以像其他类型的值一样被传递和操作,从而使代码更加灵活和简洁。

在 Kotlin 中,我们可以使用 lambda 表达式来表示函数,并将其作为参数传递给高阶函数。高阶函数的声明和使用方式与普通函数类似,只是在函数的参数或返回值中包含了函数类型。

以下是一个简单的示例,展示了如何定义和使用一个高阶函数:

// 高阶函数,接收一个函数作为参数,并执行该函数
fun calculate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
    return operation(a, b)
}

// Lambda 表达式,表示一个加法操作
val add: (Int, Int) -> Int = { x, y -> x + y }

// Lambda 表达式,表示一个乘法操作
val multiply: (Int, Int) -> Int = { x, y -> x * y }

fun main() {
    val resultAdd = calculate(10, 5, add) // 调用高阶函数,执行加法操作,结果为 15
    val resultMultiply = calculate(10, 5, multiply) // 调用高阶函数,执行乘法操作,结果为 50

    println("Addition result: $resultAdd")
    println("Multiplication result: $resultMultiply")
}

在上面的示例中,我们定义了一个高阶函数 calculate,它接收两个整数 ab,还有一个函数参数 operation,该参数表示一个接收两个整数并返回一个整数的函数。在 calculate 函数中,我们将 operation 参数作为函数调用,传递实际的计算操作。

然后,我们定义了两个 lambda 表达式 addmultiply,分别表示加法和乘法操作。最后,在 main 函数中,我们调用了 calculate 高阶函数,并传递了 addmultiply lambda 表达式作为参数,执行了不同的计算操作。

通过高阶函数和 lambda 表达式的组合,我们可以编写更具表达力和灵活性的代码,从而使得 Kotlin 中的函数更加强大和易于使用。在 Android 开发中,高阶函数常用于处理集合数据、异步操作和回调处理等场景。


1.8.2 kotlin高阶函数在Android中的使用

在 Android 开发中,Kotlin 高阶函数被广泛用于处理异步操作、集合数据以及 UI 事件的回调处理等场景。使用高阶函数可以使代码更加简洁、灵活,并提高代码的可读性和维护性。下面列举几个在 Android 中使用 Kotlin 高阶函数的常见情况:

  1. 异步操作和回调处理:

在 Android 开发中,经常需要进行异步操作,比如网络请求或数据库查询。通常我们使用回调方式来处理异步操作的结果。使用高阶函数,可以简化回调的处理过程。

fun doAsyncOperation(callback: (result: String) -> Unit) {
    // 模拟异步操作,这里使用延迟 2 秒模拟耗时操作
    Handler(Looper.getMainLooper()).postDelayed({
        callback("Async operation result")
    }, 2000)
}

// 在 Activity 中调用异步操作并处理结果
fun performAsyncOperation() {
    doAsyncOperation { result ->
        // 处理异步操作的结果
        textView.text = result
    }
}

在上面的例子中,doAsyncOperation 是一个模拟异步操作的高阶函数,它接收一个回调函数 callback,在操作完成后通过该回调函数传递结果。在 performAsyncOperation 方法中,我们调用 doAsyncOperation 并传递一个 lambda 表达式作为回调处理结果。

  1. 集合数据的处理:

在 Android 中,经常需要对集合数据进行过滤、映射、排序等操作。使用高阶函数可以使这些操作更加简洁和优雅。

data class Item(val name: String, val price: Double)

// 过滤价格大于 50 的商品
fun filterExpensiveItems(items: List<Item>): List<Item> {
    return items.filter { it.price > 50 }
}

// 映射商品列表为商品名称列表
fun mapItemNames(items: List<Item>): List<String> {
    return items.map { it.name }
}

// 对商品列表按价格进行排序
fun sortItemsByPrice(items: List<Item>): List<Item> {
    return items.sortedBy { it.price }
}

// 在 Activity 中使用高阶函数处理商品列表
val itemList = listOf(Item("Item1", 30.0), Item("Item2", 60.0), Item("Item3", 40.0))

val expensiveItems = filterExpensiveItems(itemList)
val itemNames = mapItemNames(itemList)
val sortedItems = sortItemsByPrice(itemList)

在上面的例子中,我们定义了几个高阶函数来处理商品列表。filterExpensiveItems 函数过滤价格大于 50 的商品,mapItemNames 函数映射商品列表为商品名称列表,sortItemsByPrice 函数对商品列表按价格进行排序。在 Activity 中,我们可以直接调用这些高阶函数来处理商品列表。

  1. 线程切换和异步任务管理:

在 Android 中,经常需要在主线程和后台线程之间切换,以及管理多个异步任务的执行。使用高阶函数可以简化线程切换和异步任务管理的操作。

// 将代码块在主线程执行
fun runOnUiThread(action: () -> Unit) {
    Handler(Looper.getMainLooper()).post {
        action()
    }
}

// 在后台线程执行代码块
fun runOnBackgroundThread(action: () -> Unit) {
    Thread {
        action()
    }.start()
}

// 在 Activity 中使用高阶函数进行线程切换
runOnBackgroundThread {
    // 在后台线程执行耗时操作
    // ...

    // 切换回主线程更新 UI
    runOnUiThread {
        textView.text = "Task completed"
    }
}

在上面的例子中,我们定义了两个高阶函数 runOnUiThreadrunOnBackgroundThread,用于在主线程和后台线程执行代码块。在 Activity 中,我们可以使用这些高阶函数来简化线程切换的操作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/814107.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软考A计划-系统集成项目管理工程师-项目干系人管理-上

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列 &#x1f449;关于作者 专注于Android/Unity和各种游…

简单易用的批量重命名工具,C++语言编写

它具备出色的文件重命名功能,能够让用户轻松对多个文件进行批量重命名操作。不论是添加前缀、后缀,还是替换文件名称中的特定字符,都能轻松完成。此外,该软件体积小巧、操作简单便捷,使用起来的效果出奇好。 MiniRenamer特色功能: 正则命名:支持正则命名规则,并可自定义…

【机器学习】Classification using Logistic Regression

Classification using Logistic Regression 1. 分类问题2. 线性回归方法3. 逻辑函数&#xff08;sigmod&#xff09;4.逻辑回归5. 决策边界5.1 数据集5.2 数据绘图5.3 逻辑回归与决策边界的刷新5.4 绘制决策边界 导入所需的库 import numpy as np %matplotlib widget import m…

【Linux】进程的认识

查看进程指令proc/ps/top 注意哦, 我们经常使用的指令, 像ls, touch…这些指令在启动之后本质上也是进程 proc 是内存文件系统, 存放着当前系统的实时进程信息. 每一个进程在系统中, 都会存在一个唯一的标识符(pid -> process id), 就如同学生在学校里有一个专门的学号一样…

Mac笔记本安装maven

Mac笔记本安装maven 一、通过brew安装maven 如果你的mac笔记本安装了homebrew可以使用如下命令安装 brew install maven安装完成后可以使用命令brew list maven来查看maven的安装位置 $ brew list maven /usr/local/Cellar/maven/3.6.3_1/bin/mvn /usr/local/Cellar/mave…

从零开始学Docker(三):DockerFile镜像定制

宿主机环境&#xff1a;RockyLinux 9 前言&#xff0c;定制docker镜像的方式有两种&#xff1a; 手动修改容器内容&#xff0c;然后docker commit提交容器为新的镜像通过在dockerfile中定义一系列的命令和参数构成的脚本&#xff0c;然后这些命令应用于基础镜像&#xff0c;依…

leetcode 面试题 0106.字符串压缩

⭐️ 题目描述 &#x1f31f; leetcode链接&#xff1a;面试题 0106.字符串压缩 思路&#xff1a; 开辟一个新的空间&#xff08;空间要大一点&#xff0c;因为可能压缩后的字符串比原字符串大&#xff09;&#xff0c;然后遍历原字符串统计当前字符的个数&#xff0c;再写入到…

使用vmd渲染并保存指定分辨率的图片

准备TCL脚本文件 # 设置渲染分辨率为1920x1080,600dpi render TachyonInternal out.tga width 1920 height 1080 dpi 600# 启用抗锯齿选项 display antialias on运行TCL 输入&#xff1a; source render.tcl使用Photoshop打开输出的out.tga文件并保存常用图片格式

【蓝桥杯备考资料】如何进入国赛?

目录 写在前面注意事项数组、字符串处理BigInteger日期问题DFS 2013年真题Java B组世纪末的星期马虎的算式振兴中华黄金连分数有理数类&#xff08;填空题&#xff09;三部排序&#xff08;填空题&#xff09;错误票据幸运数字带分数连号区间数 2014年真题蓝桥杯Java B组03猜字…

RK3568平台开发系列讲解(应用篇)输入设备应用编程

🚀返回专栏总目录 文章目录 一、输入设备介绍二、input 子系统三、读取输入设备四、按键应用编程沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇我们将讲解输入设备应用编程。 一、输入设备介绍 输入设备是指可以接收用户输入的设备,例如键盘、鼠标、触摸屏等…

41. linux通过yum安装postgresql

文章目录 1.下载安装包2.关闭内置PostgreSQL模块:3.安装postgresql服务:4.初始化postgresql数据库:5.设置开机自启动:6.启动postgresql数据库7.查看postgresql进程8.通过netstat命令或者lsof 监听默认端口54329.使用find命令查找了一下postgresql.conf的配置位置10.修改postgre…

保姆级秋招教程之简历篇

大家好&#xff0c;我是千寻哥&#xff0c;个人简历在程序员求职过程中扮演着至关重要的角色。 今天我将详细给大家介绍一下写简历的必备要素和布局&#xff0c;同时强调应避免的“坑”&#xff01; 希望能通过这些技巧&#xff0c;能帮助程序员打造一份出色的简历&#xff0c;…

Python - print

文章目录 1. end‘’2. 未完待续~ 1. end‘’ 如果没有end‘’&#xff0c;每次print语句都会自动换行&#xff0c;而有了这个语句&#xff0c;数据就不会自动换行&#xff0c;而是在输出的数据后面加上空格&#xff08;空格数取决于引号里面的空格数&#xff09;示例&#xf…

Vulnhub: hacksudo: search靶机

kali&#xff1a;192.168.111.111 靶机&#xff1a;192.168.111.170 信息收集 端口扫描 nmap -A -sC -v -sV -T5 -p- --scripthttp-enum 192.168.111.170 80端口目录爆破 feroxbuster -k -d 1 --url http://192.168.111.170 -w /opt/zidian/SecLists-2022.2/Discovery/Web…

计网 第三章错题整理 (3.4以后)

3.4 ③ 发送窗口不能大于接收窗口 否则窗口大小大于序号范围一般的时候 超时重传接收方没法辨别是新帧还是旧帧 本题有歧义 因为编号个数可以自定义的话&#xff0c;信道利用率都可达到百分之百 数据帧长度为128B的话 发送同样一个比特序列&#xff0c;需要更多的比特数 所以要…

7、单元测试--测试RestFul 接口

单元测试–测试RestFul 接口 – 测试用例类使用SpringBootTest(webEnvironment WebEnvironment.RANDOM_PORT)修饰。 – 测试用例类会接收容器依赖注入TestRestTemplate这个实例变量。 – 测试方法可通过TestRestTemplate来调用RESTful接口的方法。 测试用例应该定义在和被测…

【多模态】22、UniDetector | 检测开放世界中的一切!(CVPR2023)

文章目录 一、背景二、方法2.1 UniDetector 框架结构2.2 Heterogeneous Label Space Training2.3 open-world inference 三、效果3.1 数据集3.2 Object Detection in the Open World3.3 Object Detection in the Closed World3.4 Object Detection in the Wild3.5 Comparison w…

msf 渗透基础篇(基本命令)

一 Metasploit 目录结构 以 kali 为例&#xff0c;几个关键路径&#xff0c;熟悉一下&#xff0c;为了更方便的查找。 1、msf 的安装路径&#xff1a; ┌──(root㉿kali)-[/usr/share/metasploit-framework] └─# ls app documentation metasploit-framework.gemspec…

力扣 746. 使用最小花费爬楼梯

题目来源&#xff1a;https://leetcode.cn/problems/min-cost-climbing-stairs/description/ C题解1&#xff1a;动态规划。虽然我的本意是跳到第i个台阶的花费&#xff0c;但代码写着写着就歪了。。 class Solution { public:int minCostClimbingStairs(vector<int>&am…

李群李代数

0.知识回顾 描述旋转本身的矩阵叫做旋转矩阵&#xff0c;旋转矩阵是一个行列式为1的正交矩阵 &#xff0c;反之&#xff0c;行列式为1的正交矩阵也是一个旋转矩阵。将n维旋转矩阵的集合定义如下&#xff1a; S O ( n ) { R ∈ R n ∗ n ∣ R R T I &#xff0c; d e t ( R …