Kotlin運算符重載及其他約定

一萌丈、重載算術(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)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末锦援,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子剥悟,更是在濱河造成了極大的恐慌灵寺,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件懦胞,死亡現(xiàn)場離奇詭異替久,居然都是意外死亡,警方通過查閱死者的電腦和手機躏尉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進店門蚯根,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事颅拦〉儆” “怎么了?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵距帅,是天一觀的道長右锨。 經(jīng)常有香客問我,道長碌秸,這世上最難降的妖魔是什么绍移? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮讥电,結(jié)果婚禮上蹂窖,老公的妹妹穿的比我還像新娘。我一直安慰自己恩敌,他們只是感情好瞬测,可當我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著纠炮,像睡著了一般月趟。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上恢口,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天孝宗,我揣著相機與錄音,去河邊找鬼弧蝇。 笑死碳褒,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的看疗。 我是一名探鬼主播沙峻,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼两芳!你這毒婦竟也來了摔寨?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤怖辆,失蹤者是張志新(化名)和其女友劉穎是复,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體竖螃,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡淑廊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了特咆。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片季惩。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出画拾,到底是詐尸還是另有隱情啥繁,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布青抛,位于F島的核電站旗闽,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏蜜另。R本人自食惡果不足惜适室,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望举瑰。 院中可真熱鬧亭病,春花似錦、人聲如沸嘶居。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽邮屁。三九已至,卻和暖如春菠齿,著一層夾襖步出監(jiān)牢的瞬間佑吝,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工绳匀, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留芋忿,地道東北人。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓疾棵,卻偏偏與公主長得像戈钢,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子是尔,可洞房花燭夜當晚...
    茶點故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內(nèi)容