重载算术运算符
重载二元算术运算
使用operator定义plus()方法后,可以直接使用+号求和
data class Point(val x: Int, val y: Int) {operator fun plus(other: Point): Point {return Point(x + other.x, y + other.y)}
}
val p1 = Point(1, 2)
val p2 = Point(3, 4)
print(p1 + p2)
若定义为扩展函数,也可实现
data class Point(val x: Int, val y: Int) operator fun Point.plus(other: Point): Point {return Point(x + other.x, y + other.y)
}
可供选择的重载运算有如下,不会自动支持交换性
不要求两个运算数是相同类型
data class Point(val x: Int, val y: Int)
operator fun Point.times(scale: Double): Point {return Point((x * scale).toInt(), (y * scale).toInt())
}
val p = Point(10, 20)
println(p * 1.5)
返回类型也可不同于任一运算数类型
operator fun Char.times(count: Int): String {return toString().repeat(count)
}
println('a' * 3)
Kotlin中的位运算符如下
- shl一一带符号左移
- shr一一带符号右移
- ushr一一无符号右移
- and一一与
- or 一一或
- xor一一异或
- inv一一取反
println(0x0F and 0xF0)
println(0x0F or 0xF0)
重载复合赋值运算符
可通过定义plusAssign()重写+=
data class Point(var x: Int, var y: Int)
operator fun Point.plusAssign(other: Point): Unit {x += other.xy += other.y
}
val p = Point(1, 2)
p += Point(3, 4)
println(p)
系统自带函数库已经重写了该方法,将一个元素添加到可变集合
val number = ArrayList<Int>()
number += 1
number += 2
for (i in number) {println(i)
}
当使用+=时,plus()和plusAssign()都可能被调用,解决办法是
- 替换运算符为普通函数调用
- 用val代替var修饰变量,这样不能调用plusAssign()
- 若类是不可变的,则提供plus()即可,若类是可变的则提供plusAssign()
重载一元运算符
如下获取负坐标
data class Point(var x: Int, var y: Int)
operator fun Point.unaryMinus(): Point {return Point(-x, -y)
}val p = Point(1, 2)
println(-p)
可重载的一元运算符有
重载比较运算符
等号运算符:equals
在Kotlin中使用==会被转换成equals,其可用于可空类型
a == b
相当于
a?.equal(b) ?: (b == null)
equals在Any类中且已经标记为operator,所以只需要用override复写,不能实现为扩展函数,因为继承自Any类的实现始终优先于扩展函数
class Point(var x: Int, var y: Int){override fun equals(other: Any?): Boolean {if (this === other) return trueif (javaClass != other?.javaClass) return falseother as Pointif (x != other.x) return falseif (y != other.y) return falsereturn true}override fun hashCode(): Int {var result = xresult = 31 * result + yreturn result}
}
排序运算符:compateTo
比较运算符 (<、> 、<= 和 >=)将转换成compateTo,Java中实现了Comparable的类在Kotlin中都可以使用运算符
class Person(val name: String, val age: Int) : Comparable<Person> {override fun compareTo(other: Person): Int {return compareValuesBy(this, other, Person::age, Person::name)}
}
val p1 = Person("A",1)
val p2 = Person("B",1)
println(p1 < p2)
如上按照字母表顺序比较,p1 < p2等价于p1.compateTo(p2) < 0,
集合和区间
下标运算符访问元素:get和set
data class Point(var x: Int, var y: Int)
operator fun Point.get(index: Int): Int {return when (index) {0 -> x1 -> yelse -> throw IndexOutOfBoundsException("Invalid coordinate")}
}
operator fun Point.set(index: Int, value: Int) {when (index) {0 -> x = value1 -> y = valueelse -> throw IndexOutOfBoundsException("Invalid coordinate")}
}val p = Point(1, 2)
println(p[0])
p[0] = 3
println(p[0])
in和contains
如下重写contains,使用in判断点是否在矩形中
data class Point(var x: Int, var y: Int)
data class Rectangle(val upperLeft: Point, val lowerRight: Point)operator fun Rectangle.contains(p: Point): Boolean {return p.x in upperLeft.x until lowerRight.x&& p.y in upperLeft.y until lowerRight.y
}val rect = Rectangle(Point(10, 20), Point(50, 50))
println(Point(20, 30) in rect)
…和rangeTo
使用1…10创建一个[1,10]的区间,可通过rangeTo为类定义该运算符,其是Comparable的扩展函数,若一个类实现了Comparable可直接使用rangeTo
val now = LocalDate.now()
val vacation = now..now.plusDays(10)
println(now.plusWeeks(1) in vacation)
rangeTo优先级低于算术运算符,使用时最好使用括号
val n = 9
println(0..(n + 1))(0..n).forEach { println(it) }
for和iterator
在for中使用in会调用iterator方法,如遍历String,其父类CharSequence已经实现了iterator
for (c in "abc") {println(c)
}
如下在LocalDate的闭区间上定义iterator
operator fun ClosedRange<LocalDate>.iterator(): Iterator<LocalDate> =object : Iterator<LocalDate> {var current = startoverride fun hasNext() =current <= endInclusiveoverride fun next() = current.apply {current = plusDays(1)}}val newYear = LocalDate.ofYearDay(2017, 1)
val dayOff = newYear.minusDays(1)..newYear
for (day in dayOff) {println(day)
}
解构声明
解构声明允许展开单个复合值,并使用它来初始化多个单独的变量,如下
data class Point(var x: Int, var y: Int)val p = Point(10, 20)
val (x, y) = p
println(x)
println(y)
解构声明初始化变量,相当于调用名为ComponentN的函数,N是声明中变量的位置,如下
class Point(var x: Int, var y: Int) {operator fun component1() = xoperator fun component2() = y
}
解构声明主要用于展开函数返回的数据类,如下将文件和扩展名分割
data class NameComponents(val name: String, val extension: String)fun splitFilename(fullName: String): NameComponents {val result = fullName.split(".", limit = 2)return NameComponents(result[0], result[1])
}
val (name, ext) = splitFilename("demo.kt")
println(name)
println(ext)
解构声明可以在数组和集合上使用,修改上面的splitFilename方法
fun splitFilename(fullName: String): NameComponents {val (name, ext) = fullName.split(".", limit = 2)return NameComponents(name, ext)
}
解构声明可用于循环中的变量声明,如遍历map
fun printEntries(map: Map<String, String>) {for ((key, value) in map) {println("$key -> $value")}
}
val map = mapOf("1" to "A", "2" to "B")
printEntries(map)
委托
by lazy
惰性初始化直到第一次访问该属性时才创建对象,常规的做法如下
class Email()fun loadEmails(person: Person): List<Email> {Thread.sleep(5000)return listOf(Email(), Email())
}
对于上述加载耗时的操作,使用_emails来存储,而另一个email用于返回
class Person(val name: String) {private var _emails: List<Email>? = nullval email: List<Email>get() {if (_emails == null) {_emails = loadEmails(this)}return _emails!!}
}
上面代码繁琐且非线程安全,使用by lazy可以让代码简单
class Person(val name: String) {val email by lazy { loadEmails(this) }
}
实现委托
实现一个例子,当对象改变时通知监听器,Java通过PropertyChangeSupport和PropertyChangeListener实现
open class PropertyChangeAware {protected val changeSupport = PropertyChangeSupport(this)fun addPropertyChangeListener(listener: PropertyChangeListener) {changeSupport.addPropertyChangeListener(listener)}fun removePropertyChangeListener(listener: PropertyChangeListener) {changeSupport.removePropertyChangeListener(listener)}
}
Person继承上面的PropertyChangeAware,在setter时通知listener
class Person(val name: String, age: Int, salary: Int) : PropertyChangeAware() {var age: Int = ageset(newValue) {val oldValue = fieldfield = newValuechangeSupport.firePropertyChange("age", oldValue, newValue)}var salary: Int = salaryset(newValue) {val oldValue = fieldfield = newValuechangeSupport.firePropertyChange("salary", oldValue, newValue)}
}
Person添加listener,属性改变时响应
val p = Person("Tom", 20, 1000)
p.addPropertyChangeListener(PropertyChangeListener { event ->println("Property ${event.propertyName} change" +"from ${event.oldValue} to ${event.newValue}")}
)
p.age = 21
可以优化代码,将属性变化的值过程封装到ObservableProperty
class ObservableProperty(val proName: String,var propValue: Int,val changeSupport: PropertyChangeSupport
) {fun getValue(): Int = propValuefun setValue(newValue: Int) {val oldValue = propValuepropValue = newValuechangeSupport.firePropertyChange(proName, oldValue, newValue)}
}class Person(val name: String, age: Int, salary: Int) : PropertyChangeAware() {val _age = ObservableProperty("age", age, changeSupport)var age: Intget() = _age.getValue()set(value) = _age.setValue(value)val _salary = ObservableProperty("age", age, changeSupport)var salary: Intget() = _salary.getValue()set(value) = _salary.setValue(value)
}
Kotlin中委托的原理也是如此,将属性的getter和setter委托给ObservableProperty,不同的是
- setter和getter标记为operator,一个参数接受对象实例,用于读写属性,另一个用于表示属性本身,类型为KProperty
- 使用by代替之前代码中调用setter和getter的步骤
class ObservableProperty(var propValue: Int,val changeSupport: PropertyChangeSupport
) {operator fun getValue(p: Person, prop: KProperty<*>): Int = propValueoperator fun setValue(p: Person, prop: KProperty<*>, newValue: Int) {val oldValue = propValuepropValue = newValuechangeSupport.firePropertyChange(prop.name, oldValue, newValue)}
}class Person(val name: String, age: Int, salary: Int) : PropertyChangeAware() {var age: Int by ObservableProperty(age, changeSupport)var salary: Int by ObservableProperty(salary, changeSupport)
}
最后,Kotlin自带了Delegates.observable,代替我们写的ObservableProperty类,只需要传递一个Lambda通知属性值的修改
class Person(val name: String, age: Int, salary: Int) : PropertyChangeAware() {private val observer = { prop: KProperty<*>, oldValue: Int, newValues: Int ->changeSupport.firePropertyChange(prop.name, oldValue, newValues)}var age: Int by Delegates.observable(age, observer)var salary: Int by Delegates.observable(salary, observer)
}