java中不支持运算符重载,而Kotlin支持操作符重载。这些操作符在Kotlin中是约定好的固定符号。(如:加号 + 乘号 * 等),和固定的优先级。而应用这样的操作符,我们也必须使用映射固定名字的成员函数或者扩展函数(加法 plus 乘法 times)。重载操作符的函数需要用operator修饰符来标记。
运算符重载时对已有的运算符赋予新的含义,使一个运算符作用于不同类型的数据会有对应这个类型的行为。运算符重载实际上是函数重载,本质上是对运算符函数的调用,从运算符到对应函数的映射过程由编译器完成。
object OperatorNameConventions {
@JvmField val GET_VALUE = Name.identifier("getValue")
@JvmField val SET_VALUE = Name.identifier("setValue")
@JvmField val PROVIDE_DELEGATE = Name.identifier("provideDelegate")
@JvmField val EQUALS = Name.identifier("equals")
@JvmField val COMPARE_TO = Name.identifier("compareTo")
@JvmField val CONTAINS = Name.identifier("contains")
@JvmField val INVOKE = Name.identifier("invoke")
@JvmField val ITERATOR = Name.identifier("iterator")
@JvmField val GET = Name.identifier("get")
@JvmField val SET = Name.identifier("set")
@JvmField val NEXT = Name.identifier("next")
@JvmField val HAS_NEXT = Name.identifier("hasNext")
@JvmField val COMPONENT_REGEX = Regex("component\\d+")
@JvmField val AND = Name.identifier("and")
@JvmField val OR = Name.identifier("or")
@JvmField val INC = Name.identifier("inc")
@JvmField val DEC = Name.identifier("dec")
@JvmField val PLUS = Name.identifier("plus")
@JvmField val MINUS = Name.identifier("minus")
@JvmField val NOT = Name.identifier("not")
@JvmField val UNARY_MINUS = Name.identifier("unaryMinus")
@JvmField val UNARY_PLUS = Name.identifier("unaryPlus")
@JvmField val TIMES = Name.identifier("times")
@JvmField val DIV = Name.identifier("div")
@JvmField val MOD = Name.identifier("mod")
@JvmField val REM = Name.identifier("rem")
@JvmField val RANGE_TO = Name.identifier("rangeTo")
@JvmField val TIMES_ASSIGN = Name.identifier("timesAssign")
@JvmField val DIV_ASSIGN = Name.identifier("divAssign")
@JvmField val MOD_ASSIGN = Name.identifier("modAssign")
@JvmField val REM_ASSIGN = Name.identifier("remAssign")
@JvmField val PLUS_ASSIGN = Name.identifier("plusAssign")
@JvmField val MINUS_ASSIGN = Name.identifier("minusAssign")
// If you add new unary, binary or assignment operators, add it to OperatorConventions as well
@JvmField
val UNARY_OPERATION_NAMES = setOf(INC, DEC, UNARY_PLUS, UNARY_MINUS, NOT)
@JvmField
internal val SIMPLE_UNARY_OPERATION_NAMES = setOf(UNARY_PLUS, UNARY_MINUS, NOT)
@JvmField
val BINARY_OPERATION_NAMES = setOf(TIMES, PLUS, MINUS, DIV, MOD, REM, RANGE_TO)
@JvmField
internal val ASSIGNMENT_OPERATIONS = setOf(TIMES_ASSIGN, DIV_ASSIGN, MOD_ASSIGN, REM_ASSIGN, PLUS_ASSIGN, MINUS_ASSIGN)
}
public static final ImmutableBiMap UNARY_OPERATION_NAMES = ImmutableBiMap.builder()
.put(KtTokens.PLUSPLUS, INC)
.put(KtTokens.MINUSMINUS, DEC)
.put(KtTokens.PLUS, UNARY_PLUS)
.put(KtTokens.MINUS, UNARY_MINUS)
.put(KtTokens.EXCL, NOT)
.build();
public static final ImmutableBiMap BINARY_OPERATION_NAMES = ImmutableBiMap.builder()
.put(KtTokens.MUL, TIMES)
.put(KtTokens.PLUS, PLUS)
.put(KtTokens.MINUS, MINUS)
.put(KtTokens.DIV, DIV)
.put(KtTokens.PERC, REM)
.put(KtTokens.RANGE, RANGE_TO)
.build();
public static final ImmutableBiMap REM_TO_MOD_OPERATION_NAMES = ImmutableBiMap.builder()
.put(REM, MOD)
.put(REM_ASSIGN, MOD_ASSIGN)
.build();
public static final ImmutableBiMap ASSIGNMENT_OPERATIONS = ImmutableBiMap.builder()
.put(KtTokens.MULTEQ, TIMES_ASSIGN)
.put(KtTokens.DIVEQ, DIV_ASSIGN)
.put(KtTokens.PERCEQ, REM_ASSIGN)
.put(KtTokens.PLUSEQ, PLUS_ASSIGN)
.put(KtTokens.MINUSEQ, MINUS_ASSIGN)
.build();
其中,KtTokens.kt中定义了+, -, *, / , == , ! , ++, – , *= , /= 等等运算符的符号。从源码中的这一句
public static final ImmutableSet NOT_OVERLOADABLE =
ImmutableSet.of(KtTokens.ANDAND, KtTokens.OROR, KtTokens.ELVIS, KtTok
我们可以知道,Kotlin中的 && 、 || 、 ?: 、 === 、 !== 是不能被重载的。
有了操作符重载,我们可以将两个对象加起来变成另外一个对象。例如,我们自定义一个BoxInt类型。然后重载times和plus函数
class BoxInt(var i: Int) {
operator fun times(x: BoxInt) = BoxInt(i * x.i)
override fun toString(): String {
return i.toString()
}
}
operator fun BoxInt.plus(x: BoxInt) = BoxInt(this.i + x.i)
object Text {
@JvmStatic
fun main(args: Array) {
val a = BoxInt(3)
val b = BoxInt(7)
println(a + b)
println(a * b)
}
}
实际上 a+b就是两个函数的调用 println(BoxInt(3).plus(BoxInt(7)))这样会好理解一些
a+b反编译成java
public static final BoxInt plus(@NotNull BoxInt $receiver, @NotNull BoxInt x) {
//.....
return new BoxInt($receiver.getI() + x.getI());
}
二元运算符与重载函数名称映射关系如下:
二元运算符 | 重载函数名称 | 备注 |
---|---|---|
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) | 范围操作符 |
比如 1+1=2 本质上1.plus(1)
自定义类型运算符重载的作用,与内置运算符的作用是同样的声明方式,但是具体的运算逻辑的实现是自定义的。
设计一个类Complex,实现负数的基本 操作。如相加(1+2i)+(3+4i) = (4+6i);相减,相乘。
class Complex {
var real: Int = 0
var image: Int = 0
constructor()
constructor(real: Int, image: Int) {
this.real = real
this.image = image
}
}
class Complex {
var real: Int = 0
var image: Int = 0
constructor()
constructor(real: Int, image: Int) {
this.real = real
this.image = image
}
/**
* 加法 实部加上实部 ,虚部加上虚部
*/
operator fun plus(c: Complex): Complex {
return Complex(this.real + c.real, this.image + c.image)
}
/**
* 减法 实部减去实部,虚部减去虚部
*/
operator fun minus(c: Complex): Complex {
return Complex(this.real - c.real, this.image - c.image)
}
/**
* 乘法
*/
operator fun times(c: Complex): Complex {
return Complex(this.real * c.real - this.image * c.image, this.real * c.image + this.image * c.real)
}
override fun toString(): String {
val img = if (image >= 0) {
"+ ${image}i"
} else {
"${image}i"
}
return "${real} ${img}"
}
}
测试代码如下
object Text {
@JvmStatic
fun main(args: Array) {
val c1 = Complex(1, 2)
val c2 = Complex(3, 4)
val p = c1 + c2
val m = c1 - c2
val t = c1 * c2
println(p)
println(m)
println(t)
}
}
输出如下:
4 + 6i
-2 -2i
-5 + 10i
其中 < 会被映射成b1 compareTo(BigDecimal) < 0的值
表达式 | 翻译成函数调用 |
---|---|
a > b | a.compareTo(b) > 0 |
a < b | a.compareTo(b) < 0 |
a >= b | a.compareTo(b) >= 0 |
a >= b | a.compareTo(b) >= 0 |
表达式 | 翻译成运算符重载函数的调用 |
---|---|
a += b | a.plusAssign(b) |
a -= b | a.minusAssign(b) |
a *= b | a.timesAssign(b) |
a /= b | a.divAssign(b) |
a %= b | a.remAssign(b) |
《Kotlin从入门到进阶实战》