前言
在 Kotlin 中,我們可以用 約定的操作符,代替 調(diào)用代碼中以特定的命名定義的函數(shù),來實現(xiàn) 與之對應(yīng)的操作。例如在類中定義了一個名為 plus 的特殊方法,就可以使用加法運算符 + 代替 plus() 的方法調(diào)用。由于你無法修改已有的接口定義,因此一般可以通過 擴展函數(shù) 來為現(xiàn)有的類增添新的 約定方法,從而使得 操作符重載 這一語法糖適應(yīng)任何現(xiàn)有的 Java 類。
算術(shù)運算符
我們就從最簡單直接的例子 + 這一類算術(shù)運算符開始。
data class Point(val x: Int, val y: Int) { operator fun plus(other: Point) = Point(x + other.x, y + other.y) operator fun plus(value: Int) = "toString: ${Point(x + value, y + value)}"}fun main(args: Array<String>) { val p1 = Point(1, 2) val p2 = Point(3, 4) println(p1 + p2) println(p1 + 3)}/*Point(x=4, y=6)toString: Point(x=4, y=5)*/// 第一個文件:package package0operator fun Point.times(value: Int) = Point(x * value, y * value)// 第二個文件:package package1operator fun Point.times(value: Int) = Unit // Do nothing.// 使用第一個擴展操作符:import package0.timesval newPoint = Point(1, 2) * 3
Kotlin 為一些基本類型預(yù)定義了一些操作符方法,我們平時常寫的基本數(shù)據(jù)計算也可以翻譯成調(diào)用這些操作符方法,比如 (2 + 3) * 4 可以翻譯成 2.plus(3).times(4),2 + 3 * 4 可以翻譯成 2.plus(3.times(4))。根據(jù)擴展函數(shù)的語法,擴展函數(shù)無法覆蓋與類已有的方法簽名相同的方法,因此,不必擔(dān)心隨隨便便給 Int 自定義一個 plus 擴展方法就能讓 1 + 1 變得不等于 2。
同時,所有操作符都針對基本類型做了優(yōu)化,比如 1 + 2 * 3、4 < 5,不會為它們引入函數(shù)調(diào)用的開銷。
所有可重載的算術(shù)運算符有:
| 表達式 | 翻譯為 |
|---|---|
| a + b | a.plus(b) |
| a - b | a.minus(b) |
| a * b | a.times(b) |
| a / b | a.div(b) |
| a % b | a.rem(b)、 a.mod(b) (在 Kotlin 1.1 中被棄用) |
| a..b | a.rangeTo(b) |
它們的優(yōu)先級與普通的數(shù)字類型運算符優(yōu)先級相同。其中 rangeTo 會在下面說明。
廣義賦值操作符
| 表達式 | 翻譯為 |
|---|---|
| a += b | a.plusAssign(b) |
| a -= b | a.minusAssign(b) |
| a *= b | a.timesAssign(b) |
| a /= b | a.divAssign(b) |
| a %= b | a.remAssign(b)、 |
對于以上廣義賦值操作符:
data class Size(var width: Int = 0, var height: Int = 0) { operator fun plus(other: Size): Size { return Size(width + other.width, height + other.height) } operator fun plusAssign(other: Size) { width += other.width height += other.height }}fun main(args: Array<String>) {// var s1 = Size(1, 2) // 如果這么寫,執(zhí)行 += 時會報錯. val s1 = Size(1, 2) val s2 = Size(3, 4) s1 += s2}我們使用這個例子來理解:為什么使用 var 定義的 s1 會導(dǎo)致 += 報錯呢?因為理論上,執(zhí)行 += 時,既可以調(diào)用 s1 = s1 + s2,也就是 s1 = s1.plus(s2),又可以調(diào)用 s1.plusAssign(s2),都符合操作符重載約定,這樣就會產(chǎn)生歧義,而如果使用 val 定義 s1,則只可能執(zhí)行 s1.plusAssign(s2),因為 s1 不可被重新賦值,因此 s1 = s1 + s2 這樣的語法是出錯的,永遠不能能調(diào)用,那么調(diào)用 s1 += s2 就不會產(chǎn)生歧義了。
既然編譯器會幫我把 a += b 解釋成 a = a + b,那是不是意味著我只需要 plus 永遠不需要 plusAssign 了呢?比較好的實踐方式是:
Kotlin 標準庫中就是這么實現(xiàn)的:
fun main(args: Array<String>) { val list = arrayListOf(1, 2) list += 3 // 添加元素到自身集合, 沒有新的對象被創(chuàng)建, 調(diào)用的是 add 方法. val newList = list + 4 // 創(chuàng)建一個新的 ArrayList, 添加自身元素和新元素并返回新的 ArrayList.}in
| 表達式 | 翻譯為 |
|---|---|
| a in b | b.contains(a) |
| a !in b | !b.contains(a) |
println("hello" in arrayListOf("hello", ", ", "world"))/*true*/在 for 循環(huán)中使用 in 操作符會執(zhí)行迭代操作,for(x in list) { /* 遍歷 */ } 將被轉(zhuǎn)換成 list.iterator() 的調(diào)用,然后在上面重復(fù)調(diào)用hasNext 和 next 方法。
rangeTo
rangeTo 用于創(chuàng)建一個區(qū)間。例如 1..10 也就是 1.rangeTo(10) 代表了從 1 到 10 這 10 個數(shù)字,Int.rangeTo 方法返回一個 IntRange 對象,IntRange 類定義如下:
/** * A range of values of type `Int`. */public class IntRange(start: Int, endInclusive: Int) : IntProgression(start, endInclusive, 1), ClosedRange<Int> { override val start: Int get() = first override val endInclusive: Int get() = last override fun contains(value: Int): Boolean = first <= value && value <= last override fun isEmpty(): Boolean = first > last override fun equals(other: Any?): Boolean = other is IntRange && (isEmpty() && other.isEmpty() || first == other.first && last == other.last) override fun hashCode(): Int = if (isEmpty()) -1 else (31 * first + last) override fun toString(): String = "$first..$last" companion object { /** An empty range of values of type Int. */ public val EMPTY: IntRange = IntRange(1, 0) }}它的基類 IntProgression 實現(xiàn)了 Iterable 接口,因此 1..10 可以用來迭代:
for (index in 1..10) { // 遍歷 1 到 10, 包括 1 和 10.}IntRange 還實現(xiàn)了接口 ClosedRange ,可以用來判斷某元素是否屬于該區(qū)間。
Kotlin 為 Comparable 定義了擴展函數(shù) rangeTo:
/** * Creates a range from this [Comparable] value to the specified [that] value. * * This value needs to be smaller than [that] value, otherwise the returned range will be empty. * @sample samples.ranges.Ranges.rangeFromComparable */public operator fun <T: Comparable<T>> T.rangeTo(that: T): ClosedRange<T> = ComparableRange(this, that)
因此所有的 Comparable 對象都可以使用 .. 區(qū)間操作符,例如:
fun main(args: Array<String>) { val c1 = Calendar.getInstance() // 代表今天. val c2 = Calendar.getInstance() c2.add(Calendar.DATE, 10) // 代表 10 天后. val c3 = Calendar.getInstance() c3.add(Calendar.DATE, 3) // 代表 3 天后. val c4 = Calendar.getInstance() c4.add(Calendar.DATE, 13) // 代表 13 天后. // 判斷某日期是否在某兩個日期范圍內(nèi). println(c3 in c1..c2) println(c4 in c1..c2)}/*truefalse*/一元前綴操作符
| 表達式 | 翻譯為 |
|---|---|
| +a | a.unaryPlus() |
| -a | a.unaryMinus() |
| !a | a.not() |
data class Point(val x: Int, val y: Int)operator fun Point.unaryMinus() = Point(-x, -y)val point = Point(10, 20)println(-point)/*Point(x=-10, y=-20)*/
遞增與遞減
| 表達式 | 翻譯為 |
|---|---|
| a++ | a.inc() |
| a 主站蜘蛛池模板: 广安市| 临汾市| 岫岩| 定结县| 芦溪县| 济南市| 房山区| 颍上县| 滦南县| 宜阳县| 龙里县| 陆良县| 调兵山市| 潮州市| 石台县| 定南县| 清涧县| 奎屯市| 政和县| 岳池县| 嘉祥县| 额济纳旗| 集贤县| 浮山县| 义乌市| 克拉玛依市| 张家港市| 会宁县| 娱乐| 昌黎县| 平武县| 龙井市| 东兰县| 石渠县| 松阳县| 南宁市| 石城县| 霍州市| 永和县| 新乡市| 东源县| |