kotlin基础——重载

重载算术运算符

重载二元算术运算

使用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)
}

在这里插入图片描述

相关推荐

  1. Kotlin 运算符重载

    2024-01-05 15:12:03       8 阅读
  2. Kotlin 重写与重载

    2024-01-05 15:12:03       10 阅读
  3. KotlinKotlin基本数据类型

    2024-01-05 15:12:03       20 阅读
  4. Android : Kotlin 基础 入门

    2024-01-05 15:12:03       44 阅读
  5. Kotlin基础语法

    2024-01-05 15:12:03       34 阅读
  6. Kotlin基础学习

    2024-01-05 15:12:03       15 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-01-05 15:12:03       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-01-05 15:12:03       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-01-05 15:12:03       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-01-05 15:12:03       18 阅读

热门阅读

  1. onvif学习记录

    2024-01-05 15:12:03       41 阅读
  2. 测试:抓包工具

    2024-01-05 15:12:03       40 阅读
  3. 【2024.01.02】刷算法07

    2024-01-05 15:12:03       33 阅读
  4. Linux 命令汇总

    2024-01-05 15:12:03       32 阅读
  5. Centos9快速安装docker快速安装MySQL

    2024-01-05 15:12:03       37 阅读
  6. CentOS 9 (stream) 安装 Docker

    2024-01-05 15:12:03       36 阅读