重载算术运算符
重载二元算术运算
使用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.x
y += 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 true
if (javaClass != other?.javaClass) return false
other as Point
if (x != other.x) return false
if (y != other.y) return false
return true
}
override fun hashCode(): Int {
var result = x
result = 31 * result + y
return 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 -> x
1 -> y
else -> throw IndexOutOfBoundsException("Invalid coordinate")
}
}
operator fun Point.set(index: Int, value: Int) {
when (index) {
0 -> x = value
1 -> y = value
else -> 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 = start
override fun hasNext() =
current <= endInclusive
override 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() = x
operator 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>? = null
val 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 = age
set(newValue) {
val oldValue = field
field = newValue
changeSupport.firePropertyChange("age", oldValue, newValue)
}
var salary: Int = salary
set(newValue) {
val oldValue = field
field = newValue
changeSupport.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 = propValue
fun setValue(newValue: Int) {
val oldValue = propValue
propValue = newValue
changeSupport.firePropertyChange(proName, oldValue, newValue)
}
}
class Person(val name: String, age: Int, salary: Int) : PropertyChangeAware() {
val _age = ObservableProperty("age", age, changeSupport)
var age: Int
get() = _age.getValue()
set(value) = _age.setValue(value)
val _salary = ObservableProperty("age", age, changeSupport)
var salary: Int
get() = _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 = propValue
operator fun setValue(p: Person, prop: KProperty<*>, newValue: Int) {
val oldValue = propValue
propValue = newValue
changeSupport.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)
}