Kotlin:扩展函数和运算符重载和infix函数

820 阅读7分钟

前言

兄弟们好,经过前边三篇知识的学习及掌握,相信对于Kotlin代码已经比较熟悉了,相信我输出内容一定会帮助到各位大佬 😬😬😬😬😬😬😬😬😬😬 下面有请各位大佬观看通俗易懂Kotlin系列之第四篇文章——扩展函数和运算符重载、infix函数 发车了兄弟们GO GO GO ~ (滑稽)

1:扩展函数

首先我们需要了解什么是扩展函数,扩展函数就是在不修改某个类源码的情况下,可以打开这个类,并且向该类添加新的函数 啥?向一个类添加新的函数,并不修改源码? 身为java出生的android码畜你一定没有听过如此滑稽的事情,下边我们一起来看一下到底如何实现的 我们先来实现一个功能:一段字符串中可能包含数字、字母和特殊符号等字符,现在我们希望统计字符串中字母的数量,实现功能的代码如下:

object StringUtils {
    fun lettersCount(str: String): Int {
        var count = 0
        for (char in str) {
            if (char.isLetter()) {
                count++
            }
        }
        return count
    }
}

上边的代码是我们用传统的代码实现 接下来我们看一下如何使用扩展函数的方式将lettersCount()添加到String类当中实现 下边是定义扩展函数的语法:

fun ClassName.MethodName(param1:Int,param2:Int):Int{
  return 0
}

相对于一个普通函数,定义扩展函数,只需要在函数名的前面加上一个ClassName.的语法结构,就表示将该函数定义到指定类当中了 下边我们尝试想String类中添加一个扩展函数 首先创建一个Kotlin.kt文件 请注意之前的文章强调过 .kt也就是新建File文件主要是用来创建扩展函数和顶层函数 一般的我们建议需要向哪个类中添加扩展函数就新建这个类的.kt文件,当然扩展函数也可以定义在现有的类中,不一定要新建file文件,但是最好定义成顶层函数,这样就可以全局使用 。String代码如下:

fun String.lettersCount(): Int {
    var count = 0
    for (char in this) {
        if (char.isLetter()) {
            count++
        }
    }
    return count
}

注意这里我们将lettersCount定义成了String类的扩展函数,这样的话整代码就有了String类的上下文 调用的地方如下:

    val count = "hfdah*(……123df".lettersCount()

Kotlin的扩展函数可以让你的代码更加简洁、丰富、更加面向对象,另外Kotlin有reverse()函数 用于反转字符串,capitalize()函数用于对首字母进行大写等,这些都是Kotlin自带的扩展函数,你可以向任何类中添加扩展函数来提升你开发效率(提升开发效率?开玩笑,当然是为了装13啦😜)...

另外我们也可以使用扩展函数简化Toast的使用 下边是toast函数具体使用方法

Toast.makeText(context, "This is Toast", Toast.LENGTH_SHORT).show()

我们新建一个Toast.kt文件

fun String.showToast(context: Context, duration: Int = Toast.LENGTH_SHORT) {
 Toast.makeText(context, this, duration).show()
}
fun Int.showToast(context: Context, duration: Int = Toast.LENGTH_SHORT) {
 Toast.makeText(context, this, duration).show()
}

调用只需要:

"This is Toast".showToast(context, Toast.LENGTH_LONG)

2:运算符重载

java中有许多语言内置运算符,比如:+-*/%等 Kotlin允许我们将所有的运算符甚至其他关键字进行重载,从而拓展这些运算符和关键字的用法 Kotlin运算符重载允许任意的对象进行运算操作,但是我们也要考虑实际的意义,比如说让两个Student对象相加好像没有什么意义,但是让两个Money对象相加却存在意义了

运算符重载使用的operator关键字,只要在指定函数的前面加上operator关键字,就可以实现运算符重载功能了,指定函数是什么呢,每一个运算符对应的函数是不同的,比如说加号运算符对应的是plus()函数 ,减号运算符对应的是miuns()函数,详细请看下图

9395392-35bc56db5d4d346d.webp 下边我们来看一下运算符重载的具体语法结构

class Obj{
    operator fun plus(obj: Obj):Obj{
        //处理逻辑
    }
}

上述语法结构中关键字operator和函数名plus都是固定不变的,而接受参数和函数的返回值都是可以自己定义, 上述表示一个一个对象可以和另一个对象相加 返回一个新的对象 对应的调用方式如下

val obj1 = Obj()
val obj2 = Obj()
val obj3 = obj1 + obj2

obj1+obj2会在编译的时候转化成obj1.plus(obj2)的调用方式 下边我们实现一个让两个Money对象相加的功能

data class Money(val value: Int) {
    operator fun plus(money: Money): Int {
        return money.value + value
    }
}
  val money1 = Money(10)
  val money2 = Money(13) 
  println("money相加得到的结果是:${money1+money2}")

money相加得到的结果是:23

9395392-d49a4bae2a4c3654.webp

Kotlin允许我们对同一个运算符进行多重重载,这样我们就可以实现Money对象直接进行对数字进行相加

data class Money(val value: Int) {
    operator fun plus(money: Money): Int {
        return money.value + value
    }
    operator fun plus(i: Int): Int {
        return value + i
    }
}
    val money1 = Money(10)
    val money2 = Money(13)
    println("money相加得到的结果是:${money1 + money2}")

    println("money对象直接加数字的结果是:${money1 + 1321}")

9395392-d4fdcbf47ca7f3b4.webp 你还可以对这个Money对象进一步扩展,比如汇率转换、小数点后边金额的四舍五入等,其他的运算符重载使用请参考我们的 语法糖表达式和实际调用函数对照图,如果想使用的话,参考刚才加法运算符的重载去实现就可以了 请注意,最后一个a in b的语法糖表达式对应的实际调用函数是b.contains(a),a、b对象的 顺序是反过来的。这在语义上很好理解的 String类就对contains进行了重载,因此我们判断Hello字符串中是否存在 h 字符串的时候 可以写成下边:

if ("hello".contains("he")) {
}

借助运算符重载:

if ("he" in "hello") {
}

接下来我们再举个例子增加以下印象 我们实现一个随机生成字符串长度的函数,代码如下:

fun getRandomLenghtString(str: String): String {
    val n = (1..20).random()
    val builder = StringBuilder()
    repeat(n) {
        builder.append(str)
    }
    return builder.toString()
}

repeat(n)是Kotlin的一个标准函数 意思内部的lambda表达式可以重复运行n次 上述代码的核心思想是将传入的字符串重复N次,如果我们能用 str *N 这种写法的话直接让str字符串重复措辞是不是更爽呢 如果想要乘以一个数字,那么肯定要在String类中重载乘号运算符才行,但是String类是系统类,我们无法修改,这个时候就可以借助扩展函数向String类中添加函数,可以直接向刚才使用的使用的String.kt文件中添加

operator fun String.times(n: Int): String {
    val build = StringBuilder()
    repeat(n) {
        build.append(this)
    }
    return build.toString()
}

解释一下以上代码,首先operator是运算符重载的必须,然后times是乘号运算法所对应的,String.是扩展函数的语法 现在字符串就有了可以和一个数字相乘的功能了

    println("${"jingkaiqaq"*3}")

9395392-0b9c2c1747699f91.webp

另外String的Kotlin已经提供了一个用于将字符串重复n边的repeat()函数,因此我们的函数还可以写成如下:

operator fun String.times(n: Int)=repeat(n)

掌握了上述的功能之后,现在我们可以更改一下getRandomLenghtString代码

fun getRandomLenghtString(str: String) = str * (1..20).random()

3:infix函数

val map = mapOf("Apple" to 1, "Banana" to 2, "Orange" to 3, "Pear" to 4, "Grape" to 5)

map中我们使用 a to b 这种语法结构构件键值对,包括kotlin自带的mapof()函数,之所以我们能实现a to b 这样的语法,是因为Kotlin中提供了一种高级语法结构:infix函数, infix函数的作用是可以将编程语言函数调用的语法规则调整一下 比如a to b这样的写法,实际上等于a to (B)的写法 下面我们通过两个具体的例子来学习infix函数的用法 ####3.1:startswith函数 startswith函数可以用于判断字符串中是否从某个指定参数开头 常用方式如下:

if ("Hello Kotlin".startsWith("Hello")) {
 // 处理具体的逻辑
}

我们使用一种更具可读性的语法来表达这段代码 新建一个infix函数编写代码如下:

infix fun String.beginsWith(prefix: String) = startsWith(prefix)

我们使用一下上边的infix函数创建的beginwith函数:如下所示

if ("Hello Kotlin" beginsWith "Hello") {
 // 处理具体的逻辑
}

infix函数允许我们将函数调用时的小数点、括号等计算机相关的语法去掉,从而使用一种更接近英文的语法来编写程序,让代码更加具有可读性

infix函数由于其语法糖格式的特殊性,有两个比较严格的闲置: 1:infix函数不可以定义成顶层函数,它必须是某个类的成员函数,可以使用扩展函数的方式将他定义到某个类中, 2:infix函数只能接受一个参数,类型没有限制

接下来我们再看一个复杂一点的列子:

val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
if (list.contains("Banana")) {
 // 处理具体的逻辑
}

我们依然可以使用infix函数改造

infix fun <T> Collection<T>.has(element: T) = contains(element)

可以看到,我们给Collection接口添加了一个扩展函数,这是因为collection是java 及kotlin所有集合的总接口,因此给collection添加一个has函数,那么所有的函数都可以使用了 现在我们就可以使用如下语法来判断集合中是否存在某个值了:

val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
if (list has "Banana") {
 // 处理具体的逻辑
}

呼,扩展函数和运算符重载、infix函数讲完了,有没有感觉到Kotlin的语法非常有意思,通过这些有意思的语法可以无情的装13了

下篇文章我们就要来学习Kotlin中最最最最经典的知识——高阶函数了,兄弟坚持住,胜利的曙光就在眼前~~~ 有什么问题欢迎留言指出😜😜😜😜😜😜😜😜😜😜