内容:
- 自定义类型算数运算(二元)
- 自定义类型复合赋值运算(二元)
- 自定义一元运算符
- 自定义比较运算符
- 集合中的约定运算符
- 委托属性
一 自定义类型算数运算
这里我们定义个了点的数据类:
data class Points(val x: Double, val y: Double) {
operator fun plus(other: Points): Points {
return Points(x + other.x, y + other.y)
}
}复制代码
这个数据类里边定义一个方法,方法是用poerator关键字修饰,方法的名字是plus,方法的逻辑是两个点的坐标是相加返回一个新的点。
我们在Kotlin中就可以使用+代替调用这个方法,如:
val point1 = Points(10.0, 10.0)
val point2 = Points(5.0, 5.0)
Log.e("rrrrrrrrrr", (point1 + point2).toString())复制代码
这就是自定义类型运算符。
自定义类型运算符的写法:使用关键字poerator去修饰,方法名字是Kotlin约定好的。
注意:运算符不仅仅能声明为一个成员函数,也可以把它定义为一个扩展函数。自定义类型的运算符,基本上和与标准数字类型的运算符有着相同的优先级
那么对应不同的运算方法名字都有什么呢?
data class Points(val x: Double, val y: Double) {
//自定义加号运算符
operator fun plus(other: Points): Points {
return Points(x + other.x, y + other.y)
}
//自定义*号运算符
operator fun times(scale: Double): Points {
return Points(x * scale, y * scale)
}
}复制代码
//正确
point1*2.0
//错误,因为double类里边没有定义这个方法,除非你扩展这个方法
2.0*point1复制代码
二 自定义类型复合赋值运算
像+=、-=等这些运算符被称为复合赋值运算符。如果你定义了一个返回值为Unit,名为plusAssign的函数,Kotiin将会在用到+=运算符的地方调用它。其他二元算术运算符也有命名相似的对应函数:如minusAssign、timesAssign等。
注意:当你在代码中用到+=的时候,理论上plus和plusAssig口都可能被调用。一般不同时写。
val arr1 = arrayListOf(1, 2, 3, 4)
//这里返回一个新集合,原集合arr1还是[1, 2, 3, 4]
val arr2 = arr1 + 5
//修改原来的集合 arr1 =[1, 2, 3, 4, 6]
arr1 += 6复制代码
val arr1 = arrayListOf(1, 2, 3, 4)
val arr2: Collection<Int> = arrayListOf(5, 6, 7)
arr1 + arr2 //创建一个新集合,原集合不改变
arr1+=arr2//改变原来的集合复制代码
三自定义一元运算符
坐标取反:
operator fun unaryMinus() =Points(-x, -y)复制代码
四自定义比较运算符
五 集合中约定运算符
5.1[] 运算符
我们知道在java中获取或设置集合的值,我们是通过get或者set方法进行的。而对应的kotlin代码们我们是用运算符[]去获取或者设置的。如下代码:
val arr1 = arrayListOf(1, 2, 3, 4, 5)
//获取值
arr1[1]
//设置值
arr1[1]=10
val arr2 = hashMapOf(1 to "one", 2 to "two")
//获取值
arr2[1]
//设置值
arr2[1] = "ONE"复制代码
其实他们的本质就是[]运算符会调用get或者set方法。按照这样的逻辑,我们可以为我们自定义的Point类去实现一个这个运算符的方法。
data class Points(var x: Double, var y: Double) {
operator fun get(index: Int): Double {
return when (index) {
0 -> x
1 -> y
else -> 0.0
}
}
operator fun set(index: Int, value: Double) {
return when (index) {
0 -> x = value
1 -> y = value
else -> { }
}
}
}复制代码
这里就可以通过[] 运算符去获取或者设置值
val point1 = Points(10.0, 10.0)
Log.e("rrrrrrrr", point1[0].toString())
point1[0] = 11.0
Log.e("rrrrrrrr", point1[0].toString())复制代码
5.2 in运算符
在java中我们使用contains去判断一个集合中是否包含一个元素,而在kotlin中我们使用in关键字去判断一个对象是否在集合中。
val arr = arrayListOf(1, 12, 3, 4, 54, 5, 2)
1 in arr复制代码
in运算符对应调用的方法就是contains , in右边的对象将会调用contains函数,in左边的对象将会作为函数入参。
5.3rangeTo的约定
在java中表示一个区间我也不会,在kotlin中去表示一个区间使用..去表示,..运算符调用rangeTo函数的一个简洁方法
5.4for的约定
5.5解构声明
val point1 = Points(10.0, 10.0)
val (x, y) = point1复制代码
上边的代码就是解构声明,一个解构声明看起来像一个普通的变量声明,但它在括号中有多个变量。它也是用了约定的原理。要在解构声明中初始化每个变量,将调用名为componentN的函数,其中N是声明中变量的位置。
val x1 = point1.component1()
val y1 = point1.component2()复制代码
对于非数据类我们可以自己写下这样的约定函数:
class Client(val name: String,val address: String) {
operator fun component1() = name
operator fun component2() = address
}
复制代码
5.6 解构声明和循环
val map = TreeMap<String,String>() map.put("1","1.1")
for ((key,value) in map) {
Log.e("rrrr","key${key}===value${value}")
}复制代码
这个就是解构声明和循环的配合达到的效果。一个是迭代一个对象,另一个是用于解构声明。Kotiin标准库给map增加了一个扩展的iterator函数,用来返回map条目的法代器。
六委托属性
在第四篇的时候讲解了委托类的一些语法和一些优势,知道了kotlin对委托有着原生的支持,现在我们再去了解一下委托属性
6.1委托属性的实现原理
类的属性的get和set方法是调用别的类的get和set方法获取到的,属性的值没有储存到本类的特点叫做属性的委托。
委托需要储存值的类必须有对应的getValue 和setValue方法方法。
如下代码:
class Client(name: String) {
var name by Provider(name)
}复制代码
这里的name是委托属性,它把值储存到Provider类中,在编译的时候上边的代码会被编译成如下代码,都是自动生成的代码:
class Client(name: String) {
private val delegate = Provider(name)
var name: String
get() = delegate.getValue(....)
set(value) = delegate.setValue(....)
}复制代码
这里会生成一个新的变量,初始化值是by后边的对象,而被委托的属性调用生成新变量值得get和set方法,这就是委托属性。
对应的provider代码如下:
class Provider(var name: String)
//一个用于接收属性的实例,用来设置或读取属性,另一个用于表示属性本身
operator fun getValue(client: Client, property: KProperty<*>): String {
return name
}
//一个用于接收属性的实例,用来设置或读取属性,另一个用于表示属性本身 operator fun setValue(client: Client, property: KProperty<*>, name: String) {
this.name = name
}
}复制代码
这就是委托属性,但是每次都要写类似Provider这样的类,很麻烦,Kotlin给我们提供了更加简便的方法:
class Client(name: String) {
val address by lazy {getAll()}
private fun getAll(): String {
return "1000"
}
}复制代码
到此你已经了解了属性的委托原理,以及属性的懒委托,接下来还有一个去实现属性值变化的监听的代码:
open class PropertyChangeAware {
protected val changeSupport = PropertyChangeSupport(this)
fun addPropertyChangeListener(listener: PropertyChangeListener) {
changeSupport.addPropertyChangeListener(listener)
}
fun removePropertyChangeListener(listener: PropertyChangeListener){
changeSupport.removePropertyChangeListener(listener)
}
}
复制代码
class Person(val name: String, arg: Int, gongzi: Int) : PropertyChangeAware() {
private val observer = { prop: KProperty<*>, oldValue: Int, newValue: Int ->
changeSupport.firePropertyChange(prop.name, oldValue, newValue)
}
var arg: Int by Delegates.observable(arg, observer)
var gongzi: Int by Delegates.observable(gongzi, observer)
}复制代码
使用:
val person = Person("张三", 20, 10000)
//监听属性值的变化,然后进行回调
person.addPropertyChangeListener(PropertyChangeListener { evt -> Log.e("rrrrrrr",evt.propertyName+evt.newValue) })复制代码
小结:
- 引用类型加,减,乘,除,取模的运算逻辑自定义
- 引用类型的+= 运算符的使用,以及在集合中的使用
- 一元运算符的自定义
- 比较运算符equal方法的讲解
- 集合中[] 和 in 对用的方法
- 区间对应的方法
- 委托属性,委托属性懒加载,委托属性加上监听属性值变化回掉写法