运算符重载
Java中的运算符基本只能应用于基本类型的+-*/
,并不能对类对象进行这一系列的运算符操作,而Kotlin则赋予了我们使类对象也具有这种运算符操作,并且具体操作我们可以自定义。
假设我们存在一个类:Balance
代表用户的余额,我们需要在其内部新增一个方法,并使用operator
关键词进行修饰
注意,函数名是有要求的,具体的操作符需要的函数名是不一致的,具体操作符与函数名的映射请参加下文。
kotlin
class Balance(val value: Int) {
operator fun plus(m : Balance) = Balance(this.value + m.value)
}
在这段代码中我们新增了plus函数,它会在Balance对象调用+
操作时,实际调用的是Balance内部这个plus方法,实例如下:
kotlin
fun main() {
val a = Balance(5)
val b = Balance(1)
val c = a + b
println(c.value)
}
输出结果:6
运算符表达式和实际调用函数对照表
运算符 | 实际调用函数 |
---|---|
a+b | a.plus(b) |
a - b | a.minus(b) |
a * b | a.times(b) |
a / b | a.div(b) |
a % b | a.rem(b) |
a++ | a.inc() |
a-- | a.dec() |
+a | a.unaryPlus() |
-a | a.unaryMinus() |
!a | a.not() |
a == b | a.equals(b) |
a > b | a.equals(b) |
a < b | a.equals(b) |
a >= b | a.equals(b) |
a <= b | a.compareTo(b) |
a...b | a.rangeTo(b) |
a[b] | a.get(b) |
a[b] = c | a.set(b, c) |
a in b | b.contains(a) |
拓展函数与运算符重载的实践
当我们学习完拓展函数与运算符重载后,我们来举个实践例子,在开发的过程中我们有可能会让字符串的内容重复几遍,比如我想让abc这三个字符在一个字符串中重复n此,你可能会想到写个工具类再写一个for循环,但是学习完拓展函数与运算符重载之后,我们就可以理由Kotlin提供给我们的这两个语法糖来试试:
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,的操作来使字符串重复三遍
kotlin
fun main() {
println("abc" * 3)
}
// 输出结果:abcabcabc
高阶函数
从本节开始,我们将告别基础知识,转向Kotlin高级用法。
高阶函数的定义
高阶函数与Lambda是密不可分的,在Lambda中我们当时使用maxBy函数去求最长的字符串时讲到,maxBy是需要传入一个Lambda参数的,如果你想实现类似这种的,就需要用到高阶函数,那么高阶函数是什么呢?如果一个函数接收另一个函数作为参数,或者返回值的类型是 另一个函数,那么该函数就称为高阶函数。
在Java中我们不能将一个函数做为另外一个函数的参数,而Kotlin却增加了函数类型的概念,如果我们将这种函数类型添加到一个函数的参数声明或者返回值声明当 中,那么这就是一个高阶函数了。
高阶函数语法规则:
kotlin
(String, Int) -> Unit
函数括号内是该函数需要接收什么参数,以及它的返回值是什么,这里的Unit相当于Java中的void关键字,将上述声明添加到方法中,那么此方法就称为高阶函数。
kotlin
fun times(func : (String, Int) -> Unit) {
func("hello", 123)
}
高阶函数允许让函数类型的参数来决定函数的执行逻辑。即使是同一个高阶函数,只要传入不同的函数类型参数,那么它的执行逻辑和最终的返回结果就可能是完全不同的
高阶函数实践
知道了高阶函数能够做什么之后,我们来尝试自定义一个我们自己的高阶函数。
定义函数num1AndNum2,参数1为数字1,参数2为数字2,参数3为对应的高阶函数操作,我们在这个方法内返回了这个函数参数的调用,这意味着这个函数的结果是由我们传递的函数参数所决定的。
kotlin
fun num1AndNum2(n1 : Int, n2 : Int, oper : (Int, Int) -> Int) : Int {
return oper(n1, n2)
}
然后在HigherOrderFunction.kt
文件中添加以下内容,其中plus主要返回n1和n2的相加结果,minus返回n1-n2的结果:
kotlin
fun plus(n1 : Int, n2 : Int): Int {
return n1 + n2
}
fun minus(n1 : Int, n2 : Int): Int {
return n1 - n2
}
接着,我们将参数和具体的函数参数传递给num1AndNum2这个函数
kotlin
fun main() {
println(num1AndNum2(1, 2, ::plus))
}
输出结果:3
但是你会想,这样也太麻烦了吧,我为什么不直接去调用HigherOrderFunction.kt
,但是函数参数不仅可以使用函数引用的方式(上文提到的方式),还可以使用Lambda、匿名函数、成员引用等,比如我们可以不用在HigherOrderFunction.kt
中定义函数,而是在调用num1AndNum2时,使用Lambda效果也是一样的:
kotlin
println(num1AndNum2(1, 2) { n1, n2 -> n1 + n2 })
高阶函数与apply
使用高阶函数还可以实现apply提供一个对象上下文的功能,新增如下方法:
kotlin
fun StringBuffer.build(block: StringBuffer.() -> Unit): StringBuffer {
block()
return this
}
可以看到我们这里使用到了拓展函数,但是奇怪的点是函数参数并不是我们之前提到的高阶函数定义的标准格式,实际情况是,我们现在使用的才是高阶函数的标准形式,在函数参数括号前使用ClassName.
来告诉Kotlin,我的这个高阶函数是定义在哪个Class里面的。
kotlin
fun main() {
val list = listOf("a", "b", "c")
val res = StringBuffer().build {
append("开始完成任务... \n")
for (s in list) {
append("正在完成: $s \n")
}
}
println(res)
}
然后我们就可以不用在使用apply来提供一个对象的上下文了。