操作符
首先要清楚常见的操作符有哪些?
对一元操作符来说包括'!'、'++'、'--',我们常用的是非操作和自增操作。
我们更熟悉二元操作符,比如a + b
在这个场景下'+'就是二元操作符,常见的二元操作符就是加减乘除等算术符号。
在Kotlin中还增加了其他二元操作符,比如:in/!in(范围操作符)。
另外还有一些其他的操作符,比如索引访问操作符a[i]、调用操作符a()。
操作符重载
你是否想过类似println("s" + 1)
这样的代码为何会编译通过,一个String类型和一个Int类型是如何相加的?为什么String类型可以像数组一样可以通过下标访问字符sss[1]
?
没错,无论'+' 还是取索引都是一种操作符,Kotlin支持操作符的重载,也就是可以将不同类型的对象进行算术运算或其它运算。
我们看看Kotling中String的源码。
public class String : Comparable<String>, CharSequence {
companion object {}
//+操作符重载
public operator fun plus(other: Any?): String
//索引访问操作符重载
public override fun get(index: Int): Char
...
}
操作符重载方法需声明operator
关键字,plus方法对应'+'操作符,参数声明为Any?,可见正式因为String重载了'+'操作符,且参数为Any?,所以在代码中可以用一个String类型的对象"+" 任意一个对象。
接下来看一下操作符对应的重载方法名。
一元操作符 | 对应方法 |
---|---|
+a | a.unaryPlus() |
-a | a.unaryMinus() |
!a | a.not() |
a++/++a | a.inc() |
a--/--a | a.dec() |
二元操作符 | 对应方法 |
---|---|
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..b | a.rangeTo(b) |
a in b | b.contains(a) |
a !in b | !b.contains(a) |
a > b | a.compareTo(b) > 0 |
回到上面的例子,如果我们稍微调整一下代码
println(1+"s")
这样就会编译报错,可以想见Int类并不支持plus且参数为String的重载。
我们看一下Int类关于plus的重载函数。
# Int
/** Adds the other value to this value. */
public operator fun plus(other: Byte): Int
/** Adds the other value to this value. */
public operator fun plus(other: Short): Int
/** Adds the other value to this value. */
public operator fun plus(other: Int): Int
/** Adds the other value to this value. */
public operator fun plus(other: Long): Long
/** Adds the other value to this value. */
public operator fun plus(other: Float): Float
/** Adds the other value to this value. */
public operator fun plus(other: Double): Double
通过重载函数的声明可以确认Int类型只能与一个数字相 '+'。
接下来我们举几个例子加深理解。
class Number constructor(var value: Int)
// 重载一元操作符+,使其对Number中实际数据取绝对值
operator fun Number.unaryPlus(): Number {
this.value = Math.abs(value)
return this
}
// 非运算取相反数
operator fun Number.not(): Number {
this.value = -value
return this
}
// 重载Number类的加法运算符 但并不做真正的加法
operator fun Number.plus(value: Int): Number {
return Number(this.value)
}
// 重载Number类的加法运算符 支持Number类型
operator fun Number.plus(value: Number): Number {
return Number(this.value)
}
// 重载Number类的索引运算 返回某个位上的数字
operator fun Number.get(index: Int): Int {
return this.value / Math.pow(10.toDouble(), index.toDouble()).toInt() % 10
}
测试函数
@JvmStatic
fun main(args: Array<String>) {
val number1 = Number(-3)
val number2 = Number(2)
println("Number value = ${number1.value}")
println("一元加法运算 value = ${(+number1).value}")
println("二元加法运算 value = ${(number1 + number2).value}")
val number3 = Number(876543210)
println("索引 number3 = ${number3[6]}")
}
//输出
Number value = -3
一元加法运算 value = 3
二元加法运算 value = 3
索引 number3 = 6
中缀调用
上面的例子都比较好理解,操作符也是我们常见的,试想一下Kotlin是否支持自定义的操作符呢?回答是肯定的,它是Kotlin语言的一大特性。
来看一个栗子:
//定义String1类
class String1(var str: String)
//声明中缀调用符"love"
infix fun String1.love(other: String1): String {
// return this.str + "love" + other.str//理想情况
return if(other.str == "you") "gun" else "meizizi" //实际情况
}
使用infix
关键字声明中缀函数,中缀符为'love',根据传入参数判断返回值,只要不是'you',都是美滋滋。如果是'you',心拔凉拔凉的。另需注意中缀函数也是扩展函数的一种,需在顶层包内声明。
来测试一下看看效果。
@JvmStatic
fun main(args: Array<String>) {
val I = String1("i")
val YOU = String1("you")
//两个String1类型之间支持中缀符'love'
print(I love YOU)
}
//输出 gun