一萌丈、重載算術(shù)運算符
1.1 重載二元算術(shù)運算
kotlin 允許我們重載常用的二元算術(shù)運算:+ - * / 赞哗,這樣我們這些基本運算就不只是能運用于基本數(shù)據(jù)結(jié)構(gòu)(int,string 等)了,我們還可以用這些符號操作對象辆雾,集合等肪笋。比如對象A+對象B,往集合C添加元素等度迂。
重載對象相加運算符寫法如下:
data class Point(val x: Int, val y: Int) {
//operator關(guān)鍵字就是重載運算符標識 plus 對應(yīng)+ 不可隨意更名
operator fun plus(p: Point): Point {
return Point(x + p.x, y + p.y)
}
}
我們建立一個Point類重載plus 方法藤乙,這樣我們可以在外部調(diào)用類的方法讓兩個對象相加:
@Test
fun testSecondPoint() {
val p1 = Point(10, 20)
val p2 = Point(30, 40)
print(p1 + p2)
print("\n" + p1.plus(p2))
}
打印結(jié)果如下:
Point(x=40, y=60)
Point(x=40, y=60)
Process finished with exit code 0
除了把Point類聲明為成員函數(shù)外,還可以聲明為擴展函數(shù)英岭,對上面的類進行如下更改即可:
data class Point(val x: Int, val y: Int) {
//operator關(guān)鍵字就是重載運算符標識 plus 對應(yīng)+ 不可隨意更名
// operator fun plus(p: Point): Point {
// return Point(x + p.x, y + p.y)
// }
}
//把方法寫到類的外面去湾盒,等同于java的靜態(tài)函數(shù)
operator fun Point.plus(p: Point): Point {
return Point(x + p.x, y + p.y)
}
這種寫法和第一種寫法是等價的,這種寫法用的更多一些诅妹。在Kotlin 中你不能定義自己的運算符罚勾,只能重載系統(tǒng)已經(jīng)有的運算符,可重載的二元運算符如下:
a*b times //對應(yīng)的方法
a/b div
a%b rem
a+b plus
a-b minus
移位運算:
kotlin沒有為標準數(shù)字類型定義任何位運算符吭狡,但提供了一些函數(shù)供我們使用:
shl -------帶符號左移
shr -------帶符號右移
ushr -------無符號右移
and --------按位與
or ----------按位或
xor ----------按位異或
inv ----------按位取反
下面舉個例子:
@Test
fun testWei() {
print(0x01 and 0x10)
print("\n")
print(0x02 or 0x10)
print("\n")
print(0x12)
print("\n")
print(0x02 or 0x10)
print("\n")
print(4 shl 1)
print("\n")
print(4 shr 2)
print("\n")
print(0x01 ushr 0x10)
}
0
18
18
18
8
1
0
Process finished with exit code 0
print(4 shl 1) 代表 100左移1位---1000 = 8
print(4 shr 2) 代表 100右移2位---001 = 1
1.2 重載復(fù)合賦值運算符
上面的二元運算符對于合并步驟的操作也是有效的尖殃,比如 += -=,看如下例子:
var point = Point(10, 20) //這里point 需要改為變量划煮,常量就不能相加了
point += Point(2, 3)
print(point)
打印如下:
Point(x=12, y=23)
Process finished with exit code 0
kotlin系統(tǒng)為這種復(fù)合運算也定義了運算符送丰,就是plusAssign,minusAssign等。
我們重載一個試試:
operator fun Point.plusAssign(p: Point) {
this.x += p.x
this.y += p.y
}
data class Point(var x: Int, var y: Int)
@Test
fun testValue() {
val point = Point(10, 20)
val pointB = Point(5, 5)
point.plusAssign(pointB)
print(point)
}
結(jié)果:
Point(x=15, y=25)
Process finished with exit code 0
注意這里的Point類的x,y必須聲明為變量弛秋,否則會報錯器躏。
1.3 重載一元運算符
常見的一運算符如下:
表達式 | 函數(shù)名 |
---|---|
+a | unaryPlus |
-a | unaryMinus |
!a | not |
++ a, a ++ | inc |
-- a, a -- | dec |
現(xiàn)在讓我們拿inc來試一下:
operator fun Point.inc(): Point {
return Point(++x, ++y)
}
fun testInc(){
var point = Point(10, 20)
point.inc()
print(point)
}
輸出:
Point(x=11, y=21)
Process finished with exit code 0
二俐载、重載比較運算符
與算術(shù)運算符一樣,我們也可以對任何對象使用比較運算符登失,而不僅僅限于基本數(shù)據(jù)類型遏佣。
2.1 等號運算符 'equals'
在kotlin中,所有== , !=運算符揽浙,都會轉(zhuǎn)換成equals方法的調(diào)用状婶。如下:
a==b ------------------>a?.equals(b)?:(b==null)
比較 a 和 b 會檢查 a是否非空,如果非空馅巷,調(diào)用a.equals(b) ,如果兩個都為空膛虫,則返回true,equal函數(shù)是系統(tǒng)幫我們寫好的钓猬,我們直接調(diào)用就行稍刀。如果我們需要手動去更改里面的比較邏輯,我們重寫一下equals這個函數(shù)看一下:
data class Point(var x: Int, var y: Int) {
override fun equals(obj: Any?): Boolean {
if (obj === this) return true
if (obj !is Point) return false
return obj.x == x && obj.y == y
}
}
@Test
fun testequals() {
print(Point(10, 15) == Point(10, 15))
print("\n")
print(Point(10, 15) != Point(20, 30))
print("\n")
print(null == Point(10, 15))
}
輸出:
true
true
false
Process finished with exit code 0
注意:Kotlin 中所有對象都支持等式比較逗噩,在Any類中已經(jīng)標記了operator,我們不需要再次標記掉丽,我們需要重寫,添加override關(guān)鍵字异雁;還需要注意這里的equals方法不能實現(xiàn)為擴展函數(shù)捶障,因為繼承自Any類的實現(xiàn)始終優(yōu)先于擴展函數(shù)。
2.2 排序運算符 'compareTo'
Kotlin中基礎(chǔ)數(shù)據(jù)比較大小可以直接進行比較纲刀,比如:int项炼、string ;但是對象的比較的話和java類似示绊,都需要實現(xiàn) Comparable接口锭部,重寫compareTo 方法,如下:
data class Point(var x: Int, var y: Int) : Comparable<Point> {
override fun compareTo(obj: Point): Int {
return compareValuesBy(this, obj, Point::x, Point::y)
}
}
tip:a>=b-----------> a.compareTo(b)>=0 (兩個對象的比較轉(zhuǎn)換為compareTo的函數(shù)調(diào)用面褐,然后結(jié)果與零進行比較)
我們調(diào)用一下看下效果:
@Test
fun testcompareTo() {
print(Point(10, 15) < Point(1, 2))
print("\n")
print(Point(10, 15) <= Point(20, 3))
print("\n")
print(Point(10, 15) <= Point(10, 3))
print("\n")
}
輸出:
false
true
false
Process finished with exit code 0
可以看到我們先比較x,如果x值相等的情況拌禾,才會去比較y,有一個先后順序。
三展哭、集合與區(qū)間的約定
處理集合常見的操作是通過下標來獲取和設(shè)置元素湃窍,以及檢查元素是否屬于當前集合,所有這些操作都支持運算符語法匪傍。比如下標運算符(a[b]),是否在集合中 in 運算符您市。
3.1 通過下標來訪問元素 'get' 'set'
對于基礎(chǔ)類型,我們可以直接訪問役衡,如下
1茵休、訪問map中的元素:
@Test
fun testGetSet() {
val map = mutableMapOf(0 to "a", 1 to "b", 2 to "c", 3 to "d")
val value=map[2]
print("\nvalue:"+value)
map[1]="bbb"
print("\nmap:"+map)
}
輸出:
value:c
map:{0=a, 1=bbb, 2=c, 3=d}
Process finished with exit code 0
這種下標獲取在kotlin中會被轉(zhuǎn)換成對get,set運算符方法的調(diào)用;map和MutableMap已經(jīng)實現(xiàn)了這些方法榕莺,我們可以直接用俐芯,現(xiàn)在讓我們來實現(xiàn)一個對象的get set運算符吧:
operator fun Point.get(index: Int): Int {
return when (index) {
0 -> x
1 -> y
else -> throw IndexOutOfBoundsException("IndexOutOfBoundsException")
}
}
operator fun Point.set(index: Int, value: Int) {
return when (index) {
0 -> x = value
1 -> y = value
else -> throw IndexOutOfBoundsException("IndexOutOfBoundsException")
}
}
然后在進行調(diào)用:
@Test
fun testGetSet() {
val map = mutableMapOf(0 to "a", 1 to "b", 2 to "c", 3 to "d")
val value = map[2]
print("\nvalue:" + value)
map[1] = "bbb"
print("\nmap:" + map)
val p = Point(10, 20)
print("\nmap:" + p[0])
p[1] = 23
print("\nmap:${p}")
p.set(0, 5)
print("\nmap:${p}")
}
輸出:
value:c
map:{0=a, 1=bbb, 2=c, 3=d}
map:10
map:Point(x=10, y=23)
map:Point(x=5, y=23)
Process finished with exit code 0
3.2 'in' 的約定
in 運算符用來檢測某個對象是否屬于集合內(nèi),對應(yīng)的函數(shù)是contains,我們用某個點是否屬于某個矩形來做測試:
data class Rectangle(val upLeft: Point, val lowRight: Point)
operator fun Rectangle.contains(p: Point): Boolean {
return p.x in upLeft.x until lowRight.x &&
p.y in upLeft.y until lowRight.y
}
@Test
fun testIn() {
val rect = Rectangle(Point(10, 20), Point(50, 50))
val p = Point(20, 30)
val p1 = Point(5, 5)
print("\nmap:${p in rect}")
print("\nmap:${p1 in rect}")
print("\nmap:${rect.contains(p)}")
}
輸出:
map:true
map:false
map:true
Process finished with exit code 0
in 運算符會轉(zhuǎn)化為集合類對contains的調(diào)用钉鸯,所以兩者等價
3.3 rangeTo 的約定
start .. end ------->start.rangeTo(end)
這個約定主要用來表示一個區(qū)間泼各,優(yōu)先級要低于算術(shù)運算符
這個約定一般用在基礎(chǔ)類型中,如下:
@Test
fun testRangeTo() {
val a = 5
print("\nmap:${0 .. (a+1)}")
print("\nmap:${0.rangeTo(a+1)}")
}
輸出:
map:0..6
map:0..6
Process finished with exit code 0
四亏拉、結(jié)構(gòu)聲明和組件函數(shù)
4.1 結(jié)構(gòu)聲明和循環(huán)
結(jié)構(gòu)聲明:允許你展開單個復(fù)合值,并使用它來初始化多個單獨的變量
@Test
fun testXIgou() {
val p=Point(10,20)
//同時聲明兩個變量逆巍,然后用對象p給它賦值
val (x,y)=p
print("\n ${x}")
print("\n ${y}")
}
輸出:
10
20
Process finished with exit code 0
我們可以把上訴賦值過程看成:
val (a,b)=p ------------>val a=p.component1() val b=p.component2()
這個流程有點類似于java中split 方法將字符串分割及塘,我們再來看看如何分割:
data class Name(
val name: String,
val ext: String
)
fun aplit(fullName: String): Name {
val result = fullName.split(".", limit = 2)
return Name(result[0], result[1])
}
fun testXI() {
val (name,ext) = aplit("hello.txt")
print("\n ${name}")
print("\n ${ext}")
}
輸出:
hello
txt
Process finished with exit code 0
相當于我們先分割字符串,在存入對象锐极,最后在數(shù)據(jù)類中取值笙僚,kotlin只支持取前5元素
再來看下循環(huán)中如何使用:
@Test
fun testXunhuan() {
val map = mapOf("蘋果" to "水果", "土豆" to "蔬菜")
for ((key, value) in map) {
print("\n$key -> $value")
}
val str="a,b,c,d,e,f,g"
val list=str.split(",")
for (p in list) {
print("\n----$p")
}
}
蘋果 -> 水果
土豆 -> 蔬菜
----a
----b
----c
----d
----e
----f
----g
Process finished with exit code 0
五、委托屬性
5.1 委托屬性基本操作
基本語法:
class Foo {
var p:Type by Delegate()
}
屬性p將它的訪問器邏輯委托給了另一個對象:delegate類的一個新對象實例灵再。通過關(guān)鍵字 by 對其后的表達式求值來獲取這個對象肋层,關(guān)鍵字 by 可以用于獲取任何符合屬性委托約定規(guī)則的對象。
5.2 使用委托屬性:惰性初始化 和 by lazy()
惰性初始化是在第一次訪問的時候翎迁,根據(jù)需要創(chuàng)建對象的一部分栋猖。在初始化過程需要消耗大量資源的時候非常有用。如下:
class Person(val name:String) {
val emails by lazy{
//懶加載數(shù)據(jù)內(nèi)容汪榔,對于耗費資源的操作很有用
// 比如查詢數(shù)據(jù)庫蒲拉,這個是線程安全的
}
}
5.3 實現(xiàn)委托屬性
舉個例子是對某人的年齡和工資變化進行跟蹤:
首先創(chuàng)建一個類來當作被委托類,重載運算符 getvalue ,setvalue:
class ObservablePro(
var oldVaule: Int, val changeVaule: PropertyChangeSupport
) {
operator fun getValue(p: Person, prop: KProperty<*>): Int = oldVaule
operator fun setValue(p: Person, prop: KProperty<*>, newV: Int) {
val oldV = oldVaule
oldVaule = newV
changeVaule.firePropertyChange(prop.name, oldV, newV)
}
}
然后痴腌,創(chuàng)建一個類雌团,內(nèi)部實現(xiàn)構(gòu)造監(jiān)聽器以及委托給上面寫的那個類
class Person(
val name: String, age: Int, salary: Int
) {
protected val change = PropertyChangeSupport(this)
fun addListener(listener: PropertyChangeListener) {
change.addPropertyChangeListener(listener)
}
var age: Int by ObservablePro(age, change)
var salary: Int by ObservablePro(salary, change)
}
最后,外部調(diào)用初始化Person類并添加監(jiān)聽:
@Test
fun testdelegate() {
val person = Person("yang", 26, 1000)
person.addListener(PropertyChangeListener {
print("\nperson 數(shù)據(jù)有變更salary:${person.salary}\tage:${person.age}\tname:${person.name}")
})
person.age = 27
person.salary = 1200
person.age = 28
person.salary = 1400
person.age = 29
person.salary = 1500
}
輸出:
person 數(shù)據(jù)有變更salary:1000 age:27 name:yang
person 數(shù)據(jù)有變更salary:1200 age:27 name:yang
person 數(shù)據(jù)有變更salary:1200 age:28 name:yang
person 數(shù)據(jù)有變更salary:1400 age:28 name:yang
person 數(shù)據(jù)有變更salary:1400 age:29 name:yang
person 數(shù)據(jù)有變更salary:1500 age:29 name:yang
Process finished with exit code 0
可以看到隨著屬性更改后士聪,監(jiān)聽器在不斷回調(diào)