• 一起来学Kotlin:概念:7. Kotlin 函数介绍:扩展函数,中缀函数,运算符函数,带有varrag输入的函数


    一起来学Kotlin:概念:7. Kotlin 函数介绍:扩展函数,中缀函数,运算符函数,带有varrag输入的函数,内联函数

    这里介绍Kotlin的不同种类函数,包括:一般的函数表达方式,扩展函数,中缀函数,运算符函数,带有varrag输入的函数,内联函数



    1 一般函数

    直接上一些例子,都挺一目了然的。

    fun printMessage(message: String): Unit {                               
        println(message)
    }
    // 一个简单的函数,它接受一个 String 类型的参数并返回 Unit (即没有返回值)。
    
    fun printMessageWithPrefix(message: String, prefix: String = "Info") {  
        println("[$prefix] $message")
    }
    // 这个函数第二个参数带默认值,其返回类型被省略,实际上和上面这个函数一样,返回 Unit。
    
    fun sum(x: Int, y: Int): Int {                                          
        return x + y
    }
    // 返回整数的函数。
    
    fun multiply(x: Int, y: Int) = x * y                                    
    // 返回整数(推断)的单表达式函数。(single-expression function)
    
    fun main() {
        printMessage("Hello")                                               // 5                    
        printMessageWithPrefix("Hello", "Log")                              // 6
        printMessageWithPrefix("Hello")                                     // 7
        printMessageWithPrefix(prefix = "Log", message = "Hello")           // 8
        println(sum(1, 2))                                                  // 9
        println(multiply(2, 4))                                             // 10
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    2 扩展函数

    我们以如何对一个字符串计数为例,java的方式可以这么写:

    object StringUtil{
        fun letterCount(str:String):Int{
            var count=0
            for(char in str){
                if(char.isLetter()){
                    count++
                }
            }
            return count
        }
    }
    
    fun main() {
        val str="ABC123xyz!@#"
        val count=StringUtil.letterCount(str)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    对于Kotlin,我们可以使用一种更加面向对象的思维实现-将方法添加到现有类中(扩展函数)。

    fun String.letterCount(): Int {
        var count = 0
        for (char in this) {
            if (char.isLetter())
                count++
        }
        return count
    }
    
    val count1="ABC123xyz!@#".letterCount()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    3 中缀函数 (infix function)

    中缀函数为为成员函数或者扩展函数,并且只有一个参数、参数不能是可变参数且不能有默认值, 并且使用infix修饰。

    对于Kotlin的map的使用就涉及到中缀函数:

    fun main(args: Array<String>) {
        val map = mapOf(1 to "one", 2 to "two")
        map.map(::println)
    }
    //result:
    //1=one
    //2=two
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    val map = mapOf(1 to "one", 2 to "two")里的to就是一个infix function。其源码实现:

    public infix fun <A, B> A.to(that: B): 
        Pair<A, B> = Pair(this, that)
    
    • 1
    • 2

    接下来还罗列了一些中缀函数的例子:

    fun main() {
    
      infix fun Int.times(str: String) = str.repeat(this)        
      // 基于 Int 定义了一个中缀函数
      println(2 times "Bye ")                                    
      // 返回 Bye Bye
    
      val pair = "Ferrari" to "Katrina"                          
      println(pair)
      // 上面的例子可以看到,to 本来就是一个中缀函数,实现的就是 Pair 的功能
    
      infix fun String.onto(other: String) = Pair(this, other)   
      val myPair = "McLaren" onto "Lucas"
      println(myPair)
      // 这是我们自定义的 to 的调用的实现
    
      val sophia = Person("Sophia")
      val claudia = Person("Claudia")
      sophia likes claudia                                       
      // 中缀表示法也适用于成员函数(方法)
    }
    
    class Person(val name: String) {
      val likedPeople = mutableListOf<Person>()
      infix fun likes(other: Person) { likedPeople.add(other) } 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    4 运算符重载函数 (Operator Functions)

    Kotlin允许我们将所有的运算符,关键字进行重载,扩展他们的用法。

    operator fun Int.times(str: String) = str.repeat(this)       
    println(2 * "Bye ")                                          
    // times() 的运算符符号是 *,因此您可以使用 2 * "Bye" 调用该函数。
    
    operator fun String.get(range: IntRange) = substring(range) 
    // 运算符函数允许对字符串进行轻松的范围访问。
    val str = "Always forgive your enemies; nothing annoys them so much."
    println(str[0..14])                                          
    // get() 运算符启用括号访问语法。
    
    class Money(val value: Int) {
        operator fun plus(money: Money): Money {
            val sum = value + money.value
            return Money(sum)
        }
    }
    
    fun main() {
        val money1=Money(5)
        val money2=Money(10)
        val money3=money1+money2
        println(money3.value)
        // 15
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    更复杂的,运算符多重重载:

    class Money(val value: Int) {
        operator fun plus(money: Money): Money {
            val sum = value + money.value
            return Money(sum)
        }
    
        operator fun plus(newValue: Int): Money {
            val sum = value + newValue
            return Money(sum)
        }
    }
    
    fun main() {
        val money1 = Money(5)
        val money2 = Money(10)
        val money3 = money1 + money2
        val money4 = money3 + 20
        println(money3.value)   //15
        println(money4.value)   //35
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    这里附上语法糖表达式和实际调用函数对照表:

    语法糖表达式实际调用函数
    a + ba.plus(b)
    a - ba.minus(b)
    a * ba.times(b)
    a / ba.div(b)
    a % ba.rem(b)
    a ++a.inc()
    a -a.dec()
    +aa.unaryPlus()
    -aa.unaryMinus()
    !aa.not()
    a == ba.equals(b)
    a >= ba.compareTo(b)
    a … ba.rangeTo(b)
    a[b]a.get(b)
    a[b] = ca.set(b,c)
    a in bb.contains(a)

    5 vararg

    Varargs 允许我们通过用逗号分隔参数来传递任意数量的参数。

    fun printAll(vararg messages: String) {                            
        for (m in messages) println(m)
    }
    // 可变参数修饰符将参数转换为可变参数。
    printAll("Hello", "Hallo", "Salut", "Hola", "你好")                 
    // 允许使用任意数量的字符串参数调用 printAll。
    
    fun printAllWithPrefix(vararg messages: String, prefix: String) { 
        for (m in messages) println(prefix + m)
    }
    printAllWithPrefix(
        "Hello", "Hallo", "Salut", "Hola", "你好",
        prefix = "Greeting: "                                         
    )
    // 我们也可以在可变参数之后添加另一个相同类型的参数。
    
    fun log(vararg entries: String) {
        printAll(*entries)                                            
    }
    // 在运行时,可变参数只是一个数组。 要将其传递到可变参数参数中,请使用特殊的扩展运算符 *,它允许我们传入 *entries(String 的 vararg)而不是 Array
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    6 内联函数

    当一个函数被内联 inline 标注后,在调用它的地方,会把这个函数方法体中的所有代码移动到调用的地方。比如如下代码:

    // 在 main() 中调用 makeTest()
    fun main() {
        Log.i("zc_test", "main() start")
        makeTest()
        Log.i("zc_test", "main() end")
    }
    // 内联函数 makeTest()
    private inline fun makeTest() {
        Log.i("zc_test", "makeTest")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    使用 inline 编译成 java 的代码

    public final void main() {
        Log.i("zc_test", "main() start");
        int $i$f$makeTest = false;
        Log.i("zc_test", "makeTest");
        Log.i("zc_test", "main() end");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    当 makeTest() 不在被 inline 修饰时, 被编辑成 java 的代码为:

    public final void main() {
        Log.i("zc_test", "main() start");
        this.makeTest();
        Log.i("zc_test", "main() end");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    可以看到,当 makeTest() 被inline 修饰时, 在 main() 中原来调用 makeTest() 的地方被替换成了 makeTest() 里面的代码。

    什么时候应该使用内联函数?

    每次使用集合操作时,我们都会使用内联函数,比如说 map,filter 等。如此一来可以更好地实现干净和快速的函数式编程。

    val newList = listOf(1, 2, 3, 4).map { it * 2 }
    
    // compiles to
    val temp: MutableList<Int> = mutableListOf()
    for (x in listOf(1, 2, 3, 4)) {
    	temp.add(x * 2)
    }
    
    val newList = temp.toList()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    inline 能带来的性能提升,往往是在参数是 lambda 的函数上。

  • 相关阅读:
    springboot+jsp教师工作量业务数据统计与分析系统ssm高校教师电子业务档案管理系统
    【机器学习基础】无监督学习(5)——生成模型
    对话ChatGPT:AIGC时代下,分布式存储的应用与前景
    【rust/esp32】wsl2开发环境搭建与测试
    高性能MySQL实战第10讲:搭建稳固的MySQL运维体系
    8位机adc采样正弦波频率
    《软件性能测试分析与调优实践之路》第二版-手稿节选-Mysql数据库性能定位与分析
    Qt Debug信息屏蔽在什么场合下会用到?
    使用telnet和ssh登录linux
    java基础 异常
  • 原文地址:https://blog.csdn.net/zyctimes/article/details/127332773