当前位置: 首页 > article >正文

【编程语言】Kotlin快速入门 - 高阶函数与运算符重载

运算符重载

Java中的运算符基本只能应用于基本类型的+-*/,并不能对类对象进行这一系列的运算符操作,而Kotlin则赋予了我们使类对象也具有这种运算符操作,并且具体操作我们可以自定义。

假设我们存在一个类:Balance代表用户的余额,我们需要在其内部新增一个方法,并使用operator关键词进行修饰

注意,函数名是有要求的,具体的操作符需要的函数名是不一致的,具体操作符与函数名的映射请参加下文。

class Balance(val value: Int) {
    operator fun plus(m : Balance) = Balance(this.value + m.value)
}

在这段代码中我们新增了plus函数,它会在Balance对象调用+操作时,实际调用的是Balance内部这个plus方法,实例如下:

fun main() {
    val a = Balance(5)
    val b = Balance(1)
    val c = a + b
    println(c.value)
}

输出结果:6

运算符表达式和实际调用函数对照表
运算符实际调用函数
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.equals(b)
a < ba.equals(b)
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)
拓展函数与运算符重载的实践

当我们学习完拓展函数与运算符重载后,我们来举个实践例子,在开发的过程中我们有可能会让字符串的内容重复几遍,比如我想让abc这三个字符在一个字符串中重复n此,你可能会想到写个工具类再写一个for循环,但是学习完拓展函数与运算符重载之后,我们就可以理由Kotlin提供给我们的这两个语法糖来试试:

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

其中我们使用operator来告诉Kotlin此函数为运算符重载,然后使用String.times将这个函数做为String的拓展函数,在方法体内我们又使用repeat来使res重复添加n次。

然后我们就可以使用:“abc” * 3,的操作来使字符串重复三遍

fun main() {
    println("abc" * 3)
}
// 输出结果:abcabcabc

高阶函数

从本节开始,我们将告别基础知识,转向Kotlin高级用法。

高阶函数的定义

高阶函数与Lambda是密不可分的,在Lambda中我们当时使用maxBy函数去求最长的字符串时讲到,maxBy是需要传入一个Lambda参数的,如果你想实现类似这种的,就需要用到高阶函数,那么高阶函数是什么呢?如果一个函数接收另一个函数作为参数,或者返回值的类型是 另一个函数,那么该函数就称为高阶函数。

在Java中我们不能将一个函数做为另外一个函数的参数,而Kotlin却增加了函数类型的概念,如果我们将这种函数类型添加到一个函数的参数声明或者返回值声明当 中,那么这就是一个高阶函数了。

高阶函数语法规则:

(String, Int) -> Unit

函数括号内是该函数需要接收什么参数,以及它的返回值是什么,这里的Unit相当于Java中的void关键字,将上述声明添加到方法中,那么此方法就称为高阶函数。

fun times(func : (String, Int) -> Unit) {
    func("hello", 123)
}

高阶函数允许让函数类型的参数来决定函数的执行逻辑。即使是同一个高阶函数,只要传入不同的函数类型参数,那么它的执行逻辑和最终的返回结果就可能是完全不同的

高阶函数实践

知道了高阶函数能够做什么之后,我们来尝试自定义一个我们自己的高阶函数。

定义函数num1AndNum2,参数1为数字1,参数2为数字2,参数3为对应的高阶函数操作,我们在这个方法内返回了这个函数参数的调用,这意味着这个函数的结果是由我们传递的函数参数所决定的。

fun num1AndNum2(n1 : Int, n2 : Int, oper : (Int, Int) -> Int) : Int {
    return oper(n1, n2)
}

然后在HigherOrderFunction.kt文件中添加以下内容,其中plus主要返回n1和n2的相加结果,minus返回n1-n2的结果:

fun plus(n1 : Int, n2 : Int): Int {
    return n1 + n2
}

fun minus(n1 : Int, n2 : Int): Int {
    return n1 - n2
}

接着,我们将参数和具体的函数参数传递给num1AndNum2这个函数

fun main() {
    println(num1AndNum2(1, 2, ::plus))
}

输出结果:3

但是你会想,这样也太麻烦了吧,我为什么不直接去调用HigherOrderFunction.kt,但是函数参数不仅可以使用函数引用的方式(上文提到的方式),还可以使用Lambda、匿名函数、成员引用等,比如我们可以不用在HigherOrderFunction.kt中定义函数,而是在调用num1AndNum2时,使用Lambda效果也是一样的:

println(num1AndNum2(1, 2) { n1, n2 -> n1 + n2 })
高阶函数与apply

使用高阶函数还可以实现apply提供一个对象上下文的功能,新增如下方法:

fun StringBuffer.build(block: StringBuffer.() -> Unit): StringBuffer {
    block()
    return this
}

可以看到我们这里使用到了拓展函数,但是奇怪的点是函数参数并不是我们之前提到的高阶函数定义的标准格式,实际情况是,我们现在使用的才是高阶函数的标准形式,在函数参数括号前使用ClassName.来告诉Kotlin,我的这个高阶函数是定义在哪个Class里面的。

fun main() {
    val list = listOf("a", "b", "c")
    val res = StringBuffer().build {
        append("开始完成任务... \n")
        for (s in list) {
            append("正在完成: $s \n")
        }
    }
    println(res)
}

然后我们就可以不用在使用apply来提供一个对象的上下文了。


http://www.kler.cn/news/368299.html

相关文章:

  • 温泉押金原路退回系统, 押金+手牌+电子押金单——未来之窗行业应用跨平台架构
  • 【算法】递归系列:206.反转链表(两种递归实现)
  • 大数据开发扩展shell 笔记
  • 深入解析机器学习算法
  • C++题目整理
  • 我准备写一份Stable Diffusion入门指南-part1
  • PCL库中的算法封装详解
  • 数字IC后端实现Innovus |给各种IP子模块添加port buffer和antenna diode万能脚本
  • 视频编辑的创意工坊,使用视频剪辑软件将视频随机分割成两段并去声进行MP3音频和M3u8文件的生成,让视频制作更高效
  • OpenTelemetry 实际应用
  • C++ (10) 软件工程实践:塑造魔法世界的工匠
  • YOlO系列——yolo v3
  • 构建高效租房平台:Spring Boot租房管理系统详解
  • esp32学习:语音识别教程esp-skainet库的使用
  • 【AIGC】2024-ECCV-ControlNet++:通过有效的一致性反馈改进条件控制
  • LINUX基于多IP访问多网站练习
  • 计算机的错误计算(一百三十六)
  • light-task-scheduler的dockerfile打包配置
  • B+树(B树的改进)
  • (九)Proteus仿真STM32单片机硬件I2C和模拟I2C读写PCF8563时钟
  • 【路径跟踪控制:Pure Pursuit控制与车辆运动学模型】
  • Web应用框架-Django应用基础(3)-Jinja2
  • HTML 基础:构建网页结构的基石
  • Java中的反射(3)——反射的应用场景
  • 微信小程序的日期区间选择组件的封装和使用
  • 重学SpringBoot3-Spring WebFlux之SSE服务器发送事件