Kotlin操作符重載實(shí)例詳解
算數(shù)運(yùn)算操作符重載
在kotlin中我定義一個(gè)類(lèi)
data class Point(val x: Int, val y: Int)
然后實(shí)例化兩個(gè)對(duì)象
val p1 = Point(3,5) val p2 = Point(5,7)
想表示p1的元素x加上p2的元素x,p1的元素y,加上p2的元素y.然后輸出一個(gè)p3.
val p3 = Point(p1.x + p2.x, p2.y + p2.y)
以上這種寫(xiě)法沒(méi)有任何問(wèn)題。不過(guò)我們可以利用Kotlin擴(kuò)展函數(shù)簡(jiǎn)化上面的操作,我們給Point增加一個(gè)plus,并附加operator關(guān)鍵字。(增加operator的關(guān)鍵字是為了區(qū)分plus并不是一個(gè)普通的成員方法)
data class Point(val x: Int, val y: Int) { operator fun plus(other: Point): Point { return Point(x + other.x, y + other.y) } }
接下來(lái)再來(lái)實(shí)現(xiàn)上面的需求.
val p3 = p1 + p2
這樣表達(dá)起來(lái)更簡(jiǎn)潔了。 另外我們可以把plus作為Point的擴(kuò)展方法
data class Point(val x: Int, val y: Int) operator fun Point.plus(other: Point): Point { return Point(x + other.x, y + other.y) }
這種場(chǎng)景適用于Point存在于一個(gè)三方庫(kù),我們并能修改其中的內(nèi)容. Kotlin中提供了以下操作符的重載. 只需要實(shí)現(xiàn)對(duì)應(yīng)的方法即可。
之前我們定一個(gè)了plus,參數(shù)是Point,實(shí)際上對(duì)于個(gè)操作符重載并不局限于同一種類(lèi)型,接下來(lái)我們來(lái)定一個(gè)times,允許你去擴(kuò)展Ponit.
data class Point(val x: Int, val y: Int) operator fun Point.times(scale: Double): Point { return Point((x * scale).toInt(), (y * scale).toInt()) } fun main(args: Array<String>) { val p = Point(10, 20) println(p * 1.5) }
注意kotlin不支持交換性,例如我這里寫(xiě)成1.5 * p這里是不允許的。除非你去定一個(gè)
operator fun Double.times(p: Point): Point
返回類(lèi)型同樣也可以不是同一個(gè)類(lèi)型,例如, 定義一個(gè)對(duì)char類(lèi)型重載*操作符重復(fù)count后返回一個(gè)string.
operator fun Char.times(count: Int): String { return toString().repeat(count) } fun main(args: Array<String>) { println('a' * 3) }
復(fù)合運(yùn)算操作符重載
我們?cè)诰幊踢^(guò)程中通常會(huì)去把這種寫(xiě)法p = p + p1 寫(xiě)成 p += p1 這種簡(jiǎn)化寫(xiě)法,在kotlin中同樣也支持這種+=操作符自定義操作。這種自定義運(yùn)算操作符在什么場(chǎng)景下使用呢? 舉個(gè)例子,我們定義集合
val numbers = ArrayList<Int>() numbers += 42 println(numbers[0])
以上寫(xiě)法會(huì)感覺(jué)更加簡(jiǎn)潔。 我們?cè)诩现卸x操作符重載方法plusAssign(這里還有minusAssign, timesAssign等等)
operator fun <T> MutableCollection<T>.plusAssign(element: T) { this.add(element) }
不過(guò)kotlin-stblib庫(kù)已經(jīng)幫你實(shí)現(xiàn)好了關(guān)于集合的類(lèi)似操作. 在集合中+,-會(huì)累加集合原始后返回一個(gè)新的集合,如果使用+=,-=, 集合是mutable,會(huì)在本集合直接修改內(nèi)容,如果集合是read-only,會(huì)返回一個(gè)拷貝后的修改集合。(這意味著如果集合是read-only,它的聲明必須要是var, 不然它不能接受新返回拷貝后的修改集合). 你可以使用單獨(dú)的元素或者集合(類(lèi)型必須一致)進(jìn)行復(fù)合運(yùn)算符操作和算數(shù)運(yùn)算符.
val list = arrayListOf(1, 2) list += 3 val newList = list + listOf(4, 5) println(list) result : [1, 2, 3] println(newList) result : [1, 2, 3, 4, 5]
一元運(yùn)算操作符重載
我們?cè)诰幊踢^(guò)程中使用類(lèi)似++a, a++, --a, a--同樣支持運(yùn)算符重載。僅僅需要重寫(xiě)下面的操作符函數(shù)即可
舉個(gè)例子
operator fun BigDecimal.inc() = this + BigDecimal.ONE fun main(args: Array<String>) { var bd = BigDecimal.ZERO println(bd++) println(++bd) }
這里注意到我只定一個(gè)inc(),同時(shí)也是支持bd++和++bd.
比較操作符重載
kotlin中還支持==, !=, >, <操作符重載. 對(duì)于==, !=我們重寫(xiě)equals方法. 這里還是拿Point來(lái)舉栗子
data class Point(val x: Int, val y: Int)
我們這里聲明成了data類(lèi),這個(gè)關(guān)鍵字加上了編譯器會(huì)自動(dòng)幫你實(shí)現(xiàn)equals方法,我們現(xiàn)在去掉,看看自己去寫(xiě)equals怎么寫(xiě)
class Point(val x: Int, val y: Int) { override fun equals(obj: Any?): Boolean { if (obj === this) return true //1 if (obj !is Point) return false return obj.x == x && obj.y == y } } fun main(args: Array<String>) { println(Point(10, 20) == Point(10, 20)) println(Point(10, 20) != Point(5, 5)) //2 println(null == Point(1, 2)) }
這里我們需要關(guān)注一個(gè)//1 obj === this,這個(gè)===操作符是比較兩個(gè)對(duì)象的引用是否一致,實(shí)際上和java中的==是一樣的。 之前我們提到的操作符重載都會(huì)加上一個(gè)operator關(guān)鍵字,這里為什么是override?因?yàn)樗貙?xiě)了Any.class的equals方法.
這里看下//2!= 其實(shí)就是equals結(jié)果的取反. 除了=和!=之外這里還有>和<, 通過(guò)重寫(xiě)compareTo可以實(shí)現(xiàn)
class Person( val firstName: String, val lastName: String ) : Comparable<Person> { override fun compareTo(other: Person): Int { return compareValuesBy(this, other, Person::lastName, Person::firstName) } } fun main(args: Array<String>) { val p1 = Person("Alice", "Smith") val p2 = Person("Bob", "Johnson") println(p1 < p2) }
這里的compareValuesBy順便說(shuō)說(shuō),它是kotlin-stblib提供的擴(kuò)展函數(shù)
集合和區(qū)域的約定
在kotlin中我們可以使用類(lèi)似于操作數(shù)組的方法操作集合,例如a[b]這種. 對(duì)于其它類(lèi),可以自定義操作符實(shí)現(xiàn)類(lèi)似的操作
operator fun Point.get(index: Int): Int { return when(index) { 0 -> x 1 -> y else -> throw IndexOutOfBoundsException("Invalid coordinate $index") } } fun main(args: Array<String>) { val p = Point(10, 20) println(p[1]) }
我們對(duì)于賦值操作同樣也可以通過(guò)自定義operator
data class MutablePoint(var x: Int, var y: Int) operator fun MutablePoint.set(index: Int, value: Int) { when(index) { 0 -> x = value 1 -> y = value else -> throw IndexOutOfBoundsException("Invalid coordinate $index") } } fun main(args: Array<String>) { val p = MutablePoint(10, 20) p[1] = 42 println(p) }
另外一個(gè)知識(shí)點(diǎn)是自定義in操作符
in對(duì)于的contains函數(shù),判斷一個(gè)元素是否屬于一個(gè)范圍里.
data class Point(val x: Int, val 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 } fun main(args: Array<String>) { val rect = Rectangle(Point(10, 20), Point(50, 50)) println(Point(20, 30) in rect) println(Point(5, 5) in rect) }
迭代運(yùn)算符重載
我們平時(shí)使用的
for (x in list) { ... }
將被轉(zhuǎn)換為 list.iterator() 的調(diào)用,然后重復(fù)調(diào)用 hasNext 和 next 方法,就像在 Java 中一樣。 請(qǐng)注意,在 Kotlin 中,它也是一種約定,這意味著可以將迭代器方法定義為擴(kuò)展。這就解釋了為什么可以迭代常規(guī) Java 字符串:kotlin-stblib 在 Char-Sequence(String 的超類(lèi))上定義了一個(gè)擴(kuò)展函數(shù)迭代器:
operator fun CharSequence.iterator(): CharIterator >>> for (c in "abc") {}
其它類(lèi)型也可以通過(guò)自定義iterator實(shí)現(xiàn)自己類(lèi)特定的操作。
import java.util.Date import java.time.LocalDate 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) } } fun main(args: Array<String>) { val newYear = LocalDate.ofYearDay(2017, 1) val daysOff = newYear.minusDays(1)..newYear for (dayOff in daysOff) { println(dayOff) } }
解構(gòu)聲明
這個(gè)操作可以分解一個(gè)對(duì)象中成員,例如
>>> val p = Point(10, 20) >>> val (x, y) = p >>> println(x) 10 >>> println(y) 20
解構(gòu)聲明看起來(lái)有點(diǎn)像變量聲明,不過(guò)它組合了多個(gè)變量值。實(shí)際上它在kotlin中也屬于自定義操作符.去解構(gòu)多個(gè)變量需要定義componentN,N是變量的位置.
class Point(val x: Int, val y: Int) { operator fun component1() = x operator fun component2() = y }
對(duì)于data類(lèi),解構(gòu)已經(jīng)幫你聲明好了 另外解構(gòu)聲明還可以用在循環(huán)中
fun printEntries(map: Map<String, String>) { for ((key, value) in map) { println("$key -> $value") } } fun main(args: Array<String>) { val map = mapOf("Oracle" to "Java", "JetBrains" to "Kotlin") printEntries(map) }
Map中包含了擴(kuò)展方法component1,component2返回key和value. 實(shí)際上你可以將上面的循環(huán)部分翻譯成
for (entry in map.entries) { val key = entry.component1() val value = entry.component2() // ... }
總結(jié)
到此這篇關(guān)于Kotlin操作符重載的文章就介紹到這了,更多相關(guān)Kotlin操作符重載內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android事件分發(fā)機(jī)制(下) View的事件處理
這篇文章主要介紹了Android事件分發(fā)機(jī)制下篇, View的事件處理的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-01-01Android中ScrollView嵌套GridView顯示不全解決方法
這篇文章主要介紹了Android中ScrollView嵌套GridView顯示不全解決方法的相關(guān)資料,需要的朋友可以參考下2017-04-04Android仿人人客戶端滑動(dòng)菜單的側(cè)滑菜單效果
這篇文章主要介紹了Android仿人人客戶端滑動(dòng)菜單的側(cè)滑特效實(shí)現(xiàn)代碼,小編覺(jué)得挺不錯(cuò)的,分享給大家供大家參考2018-05-05Spi機(jī)制在Android開(kāi)發(fā)的應(yīng)用示例詳解
這篇文章主要為大家介紹了Spi機(jī)制在Android開(kāi)發(fā)的應(yīng)用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08設(shè)備APP開(kāi)發(fā)環(huán)境配置細(xì)節(jié)介紹
隨著工業(yè)自動(dòng)化的不斷發(fā)展,設(shè)備APP也越來(lái)越重要,本文就設(shè)備APP開(kāi)發(fā)軟件配置細(xì)節(jié)做一個(gè)深入詳解2022-09-09listView的item中有checkbox,導(dǎo)致setOnItemClick失效的原因及解決辦法
這篇文章主要介紹了listView的item中有checkbox,導(dǎo)致setOnItemClick失效的原因及解決辦法,需要的朋友可以參考下2017-01-01Android使用socket創(chuàng)建簡(jiǎn)單TCP連接的方法
這篇文章主要介紹了Android使用socket創(chuàng)建簡(jiǎn)單TCP連接的方法,結(jié)合實(shí)例形式詳細(xì)分析了Android使用socket創(chuàng)建TCP連接的具體步驟與實(shí)現(xiàn)技巧,需要的朋友可以參考下2016-04-04