Kotlin第十一讲---操作符重载&中缀表达式

内容简介

在 C 或 C++ 中是有操作符重载的概念的。在 Java 中我们认知的 +-*/ 只能在数字类型中做计算使用。而在 Kotlin 中我们可以通过操作符重载的形式改变其含义。

重载操作符的运用

其实在 Kotlin 中很多类型都已帮我们默认定义了一些操作符重载,为我们后续开发带来极大的遍历。我们可以重载一些常见的操作符,如 +-*/ 让我们代码可读性更强。

曾经我接到一个任务,将一段 Python 代码翻译成 Kotlin 代码, Python中可以对矩阵进行 +*/的运算,而 Kotlin 中只有对应的方法 plus times div 的方法,我就是通过 Kotlin 的操作符重载的功能,让其翻译的最终代码和 Python 写法上尽量保持一致。

数组相加

我定义了 2 个不可变数组,然后通过 + 将 2 个数组合并了,在 Java 的世界中,是不存在数组相加的情况。

fun main() {
    /**
     * 创建2个数组
     */
    val array1 = arrayOf("1", "2", "3")
    val array2 = arrayOf("4", "5", "6")
    val array3 = array1 + array2
    /**
     * 遍历输出
     */
    array3.forEach(::println)
}
输出结果:
1
2
3
4
5
6

List集合相加减

上面看了数组相加,集合可以吗?想一想,当然是可以的啦!我们来看下吧!

fun main() {
    /**
     * 创建2个可变集合
     */
    val list1 = mutableListOf<String>("1", "2", "3")
    val list2 = mutableListOf("1", "5", "6")
    /**
     * 集合相加
     */
    val list3 = list1 + list2
    /**
     * 遍历输出
     */
    list3.forEach(::println)
    println("========减法===========")
    /**
     * 集合相减
     */
    val minusResult = list1 - list2
    /**
     * 遍历输出减法
     */
    minusResult.forEach(::println)
}
输出结果:
1
2
3
1
5
6
========减法===========
2
3

从结果可以看出,相加是合并 2 个 List 集合。相减含义是去除减数在被减数中存在的元素。

Map 也是支持相加减的,有兴趣的可以自己去尝试。当然 Kotlin 提供的操作符重载方法还有很多,我只是随便举了个例子,其他的大家自行挖掘吧。

如何自定义重载操作符

如果想重载操作符,只需要通过关键词 operator 修饰对应方法即可,并且重载的操作符对应一个固定昵称的方法。

举个????,如果我想为我自己定义的对象重载 + 的操作符,只需要通过 operator 修饰 plus 方法即可(+ 操作符对应的就是 plus 方法)。

/**
 * 程序员实体
 */
data class CodePeople(val name: String, val age: Int)
/**
 * UI实体
 */
data class UiPeople(val name: String, val age: Int)
/**
 * 团队
 */
data class Tream(
    val codes: MutableList<CodePeople> = mutableListOf()
    , val uis: MutableList<UiPeople> = mutableListOf()
) {
    /**
     * 重载 + 团队,增加程序员方法
     */
    operator fun plus(code: CodePeople) {
        codes + code
    }
    /**
     * 重载 + 团队,增加 UI 方法
     */
    operator fun plus(ui: UiPeople) {
        uis + ui
    }
}
/**
 * 测试
 */
fun main() {
    val tream = Tream()
    /**
     * 可以看到可以调用 + 了
     */
    tream + CodePeople("阿文", 18)
    tream + UiPeople("小丽", 18)
}

通过上面的代码,我们可以注意到,在 Tream 类中重载了 2 个 plus 方法,对应的行参就是要被加的类型。其实看到这里大家应该能猜想到,重载操作符的实现原理。其实就是 Kotlin 编译器,编译时将 + 替换成调用 plus 方法。

其实我上面的例子并不是最佳写法。一般来说,重载操作符都以扩展函数的形式去定义。这样我们不光可以为自己的类做重载操作符操作,也可以为系统的类做重载操作符操作。

举个例子:

/**
 * andorid viewGroup 没有 + 操作符的的含义
 * 通过扩展函数的形式编写重载操作符
 */
operator fun ViewGroup.plus(view: View): ViewGroup {
    addView(view)
    return this
}
fun test(context: Context) {
    val frameLayout = FrameLayout(context)
    val textView = TextView(context)
    val textView2 = TextView(context)
    /**
     * 可以连续为 ViewGroup 对象,通过 + 的方式,添加 View
     */
    frameLayout + textView + textView2
}

可重载的操作符

看了上面的重载操作符是不是感觉很方便呀?我们只需要通过 operator 修饰对应的方法即可。那么对应哪些操作符可以重载呢?对应的方法名又是什么呢?接下来统计下可以重载的函数。

一元操作符

操作符对应重载函数
+aa.unaryPlus()
-aa.unaryMinus()
!aa.not()
a++a.inc()
a--a.dec()

二元操作符

操作符对应重载函数
a + ba.plus(b)
a - ba.minus(b)
a * ba.times(b)
a / ba.div(b)
a % ba.rem(b)、 a.mod(b) (已弃用)
a..ba.rangeTo(b)

in操作符

操作符对应重载函数
a in bb.contains(a)
a !in b!b.contains(a)

索引操作符

操作符对应重载函数
a[i]a.get(i)
a[i, j]a.get(i, j)
a[i1, ……, in]a.get(i1, ……, in)
a[i] = ba.set(i, b)
a[i, j] = ba.set(i, j, b)
a[i1, ……, in] = ba.set(i1, ……, in, b)

调用操作符

操作符对应重载函数
a()a.invoke()
a(i)a.invoke(i)
a(i, j)a.invoke(i, j)
a(i1, ……, in)a.invoke(i1, ……, in)

广义赋值

操作符对应重载函数
a += ba.plusAssign(b)
a -= ba.minusAssign(b)
a *= ba.timesAssign(b)
a /= ba.divAssign(b)
a %= ba.remAssign(b), a.modAssign(b)(已弃用)

相等与不等操作符

操作符对应重载函数
a == ba?.equals(b) ?: (b === null)
a != b!(a?.equals(b) ?: (b === null))

比较操作符

操作符对应重载函数
a > ba.compareTo(b) > 0
a < ba.compareTo(b) < 0
a >= ba.compareTo(b) >= 0
a <= ba.compareTo(b) <= 0

中缀表达式

上面说过重载操作符,都是重载已经存在的操作符。那我们能不能类似自定义一个操作符呢?比如将一些无意义的关键词变的有意义?中缀表达式可以完成这个功能。

中缀表达式的运用

在说中缀表达式之前,我们回顾下集合篇的 Map。我们在定义可变 Map 的时候,定义时初始化元素使用了 to,这个 to 是什么意思呢?

fun main() {
    val results = mutableMapOf<String, Int>("阿文" to 18, "小丽" to 18)
}

其实这里面的 to 就是一个中缀表达式,将 to 关键词从无意义变成了有意义。

看下 to 的定义,可以看到为任意类型增加了一个 to 的扩展方法,且方法通过 infix 修饰,返回 Pair 对象。 mutableMapOf 可以将 Pair 类型的对象增加成元素(其实就是 key 和 value 结构)。

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

自定义中缀表达式

通过前面的 to 讲解。我们知道了中缀表达式只需要通过 infix 修饰函数即可(一般来说,中缀表达式也是通过扩展函数的形式去创建)。

我们来举个例子。

/**
 * 程序员实体
 */
data class CodePeople(val name: String, val age: Int)
/**
 * UI实体
 */
data class UiPeople(val name: String, val age: Int)
/**
 * 团队
 */
data class Tream(
    val codes: MutableList<CodePeople> = mutableListOf()
    , val uis: MutableList<UiPeople> = mutableListOf()
)
/**
 * 通过扩展函数
 * 中缀表达式 merge 合并2个Tream
 */
infix fun Tream.merge(tram: Tream) {
    /**
     * 复习下操作符重载
     */
    codes + tram.codes
    uis + tram.codes
}
/**
 * 测试
 */
fun main() {
    val tream1 = Tream()
    val tream2 = Tream()
    /**
     * 合并
     */
    tream1 merge tream2
}

推荐阅读

--END--

识别二维码,关注我们

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值