亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > 綜合 > 正文

Kotlin學習教程之操作符重載詳解

2024-07-21 23:03:43
字體:
來源:轉載
供稿:網友

前言

在 Kotlin 中,我們可以用 約定的操作符,代替 調用代碼中以特定的命名定義的函數,來實現 與之對應的操作。例如在類中定義了一個名為 plus 的特殊方法,就可以使用加法運算符 + 代替 plus() 的方法調用。由于你無法修改已有的接口定義,因此一般可以通過 擴展函數 來為現有的類增添新的 約定方法,從而使得 操作符重載 這一語法糖適應任何現有的 Java 類。

算術運算符

我們就從最簡單直接的例子 + 這一類算術運算符開始。

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)*/
  • operator 修飾符是必須的,否則 plus 只是一個普通方法,不能通過 + 調用。
  • 操作符是有優先級的,比較 * 優先級高于 +,不論這個操作符應用于什么對象,這種優先級都是固定存在的。
  • plus 方法的參數類型是任意的,因此可以方法重載,但是 參數數量只能是 1 ,因為 + 是一個二元操作符。plus 方法的返回值類型也是任意的。
  • 如果出現多個方法簽名相同的 operator 擴展方法,根據 import 決定使用哪個一,例如:
// 第一個文件: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 為一些基本類型預定義了一些操作符方法,我們平時常寫的基本數據計算也可以翻譯成調用這些操作符方法,比如 (2 + 3) * 4 可以翻譯成 2.plus(3).times(4),2 + 3 * 4 可以翻譯成 2.plus(3.times(4))。根據擴展函數的語法,擴展函數無法覆蓋與類已有的方法簽名相同的方法,因此,不必擔心隨隨便便給 Int 自定義一個 plus 擴展方法就能讓 1 + 1 變得不等于 2。

同時,所有操作符都針對基本類型做了優化,比如 1 + 2 * 3、4 < 5,不會為它們引入函數調用的開銷。

所有可重載的算術運算符有:

 

表達式  翻譯為
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)

 

它們的優先級與普通的數字類型運算符優先級相同。其中 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)、 a.modAssign(b) (在 Kotlin 1.1 中被棄用)

 

對于以上廣義賦值操作符:

  • 如果對應的二元算術運算符函數也 可用 ,則報錯。plus 對應 plusAssign。minus、times 等也類似。
  • 返回值類型必須為 Unit。
  • 如果執行 a += b 時 plusAssign 不存在,會嘗試生成 a = a + b,其中的 a + b 使用的就是 plus 操作符方法,相當于調用 a = a.plus(b)。并且此時會 要求 a + b 的 plus 方法的返回值類型必須與 a 類型一致(如果單獨使用 a + 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) // 如果這么寫,執行 += 時會報錯. val s1 = Size(1, 2) val s2 = Size(3, 4) s1 += s2}

我們使用這個例子來理解:為什么使用 var 定義的 s1 會導致 += 報錯呢?因為理論上,執行 += 時,既可以調用 s1 = s1 + s2,也就是 s1 = s1.plus(s2),又可以調用 s1.plusAssign(s2),都符合操作符重載約定,這樣就會產生歧義,而如果使用 val 定義 s1,則只可能執行 s1.plusAssign(s2),因為 s1 不可被重新賦值,因此 s1 = s1 + s2 這樣的語法是出錯的,永遠不能能調用,那么調用 s1 += s2 就不會產生歧義了。

既然編譯器會幫我把 a += b 解釋成 a = a + b,那是不是意味著我只需要 plus 永遠不需要 plusAssign 了呢?比較好的實踐方式是:

  • + (plus) 始終返回一個新的對象
  • += (plusAssign) 用于內容可變的類型,修改自身的內容。

Kotlin 標準庫中就是這么實現的:

fun main(args: Array<String>) { val list = arrayListOf(1, 2) list += 3 // 添加元素到自身集合, 沒有新的對象被創建, 調用的是 add 方法. val newList = list + 4 // 創建一個新的 ArrayList, 添加自身元素和新元素并返回新的 ArrayList.}

in

 

表達式  翻譯為
a in b b.contains(a)
a !in b !b.contains(a)

 

println("hello" in arrayListOf("hello", ", ", "world"))/*true*/

在 for 循環中使用 in 操作符會執行迭代操作,for(x in list) { /* 遍歷 */ } 將被轉換成 list.iterator() 的調用,然后在上面重復調用hasNext 和 next 方法。

rangeTo

rangeTo 用于創建一個區間。例如 1..10 也就是 1.rangeTo(10) 代表了從 1 到 10 這 10 個數字,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 實現了 Iterable 接口,因此 1..10 可以用來迭代:

for (index in 1..10) { // 遍歷 1 到 10, 包括 1 和 10.}

IntRange 還實現了接口 ClosedRange ,可以用來判斷某元素是否屬于該區間。

Kotlin 為 Comparable 定義了擴展函數 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 對象都可以使用 .. 區間操作符,例如:

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 天后.  // 判斷某日期是否在某兩個日期范圍內. 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– a.dec()

 

編譯器自動支持與普通數字類型的前綴、后綴自增運算符相同的語義。例如后綴運算會先返回變量的值,然后才執行 ++ 操作。

索引訪問操作符

 

表達式  翻譯為
a[i] a.get(i)
a[i, j] a.get(i, j)
a[i_1, ……, i_n] a.get(i_1, ……, i_n)
a[i] = b a.set(i, b)
a[i, j] = b a.set(i, j, b)
a[i_1, ……, i_n] = b a.set(i_1, ……, i_n, b)

 

@Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST")operator fun <T> SharedPreferences.get(key: String, defValue: T) = when (defValue) { is String -> getString(key, defValue) is Int -> getInt(key, defValue) is Long -> getLong(key, defValue) is Float -> getFloat(key, defValue) is Boolean -> getBoolean(key, defValue) else -> throw RuntimeException()} as T@SuppressLint("CommitPrefEdits")operator fun <T> SharedPreferences.set(key: String, value: T) = with(edit()) { when (value) { is String -> putString(key, value) is Int -> putInt(key, value) is Long -> putLong(key, value) is Float -> putFloat(key, value) is Boolean -> putBoolean(key, value) else -> throw RuntimeException() }.apply()}fun main(args: Array<String>) { val version = sp["key_version", 47] // 讀 sp. sp["key_version"] = 48 // 寫 sp.}

調用操作符

 

表達式  翻譯為
a() a.invoke()
a(i) a.invoke(i)
a(i, j) a.invoke(i, j)
a(i_1, ……, i_n) a.invoke(i_1, ……, i_n)

 

相等與不等操作符

 

表達式  翻譯為
a == b a?.equals(b) ?: (b === null)
a != b !(a?.equals(b) ?: (b === null))

 

這在 Any 中被定義。Java 的 a.equals(b) 相當于 Koltin 的 a == b,Java 的 a == b 相當于 Kotlin 的 a === b(同一性檢查)。要自定義 == 操作符其實就是覆寫 equals 方法。Kotlin 中 === 不可被重載。

比較操作符

 

表達式  翻譯為
a > b a.compareTo(b) > 0
a < b a.compareTo(b) < 0
a >= b a.compareTo(b) >= 0
a <= b a.compareTo(b) <= 0

 

要求 compareTo 返回值類型必須為 Int ,這與 Comparable 接口保持一致。

data class Movie(val name: String, val score: Int, val date: Date, val other: Any = Any()) : Comparable<Movie> { override fun compareTo(other: Movie): Int {  return compareValuesBy(this, other, Movie::score, Movie::date, Movie::name) // 如果將 Movie::other 也用作比較會報錯, 因為 other 不是 Comparable 類型的。 }}fun main(args: Array<String>) { val df = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()) val movie0 = Movie("馬戲之王", 8, df.parse("2018-01-31")) val movie1 = Movie("神秘巨星", 7, df.parse("2018-01-01")) val movie2 = Movie("移動迷宮", 7, df.parse("2018-01-02")) println(movie0 < movie1) println(movie1 < movie2)}/*falsetrue*/

其中的 compareValuesBy 方法如下:

/** * Compares two values using the specified functions [selectors] to calculate the result of the comparison. * The functions are called sequentially, receive the given values [a] and [b] and return [Comparable] * objects. As soon as the [Comparable] instances returned by a function for [a] and [b] values do not * compare as equal, the result of that comparison is returned. * * @sample samples.comparisons.Comparisons.compareValuesByWithSelectors */public fun <T> compareValuesBy(a: T, b: T, vararg selectors: (T) -> Comparable<*>?): Int { require(selectors.size > 0) return compareValuesByImpl(a, b, selectors)}private fun <T> compareValuesByImpl(a: T, b: T, selectors: Array<out (T)->Comparable<*>?>): Int { for (fn in selectors) {  val v1 = fn(a)  val v2 = fn(b)  val diff = compareValues(v1, v2)  if (diff != 0) return diff } return 0}

我們定義一個 Movie 類,它實現了 Comparable 接口,在比較時,希望按照 評分 、 上映日期 、 電影名稱 的優先級順序排序??梢院唵蔚氖褂帽容^操作符對 Movie 對象進行“大小比較”。

操作符函數與 Java

Java 中調用 Kotlin 中的操作符方法,就跟調用普通方法一樣,你不能期望在 Java 中寫 new Point(1, 2) + new Point(3, 4) 這樣的語法,只能乖乖調用 new Point(1, 2).plus(new Point(3, 4))。

反之,Kotlin 中調用 Java 代碼卻可以同 Kotlin 中自定義操作符方法一樣方便。只要一個類提供了滿足操作符方法簽名的方法,哪怕它只是一個普通方法,不需要加 operator 修飾符(Java 中也沒有這個修飾符),就可以在 Kotlin 中以操作符的方式調用。例如:arrayList[0] 相當于 Java 中 arrayList.get(0),盡管這個 get 方法是 Java 中定義的。又比如所有實現了 Comparable 的類實例都可以使用比較操作符 >、< 等進行比較。

Java 中的位運算符在 Kotlin 中是沒有的 ,它們只能使用普通方法加中綴表達式使用,只能用于 Int 和 Long,對應關系如下:

 

Java 中   Kotlin 中
« 有符號左移 shl(bits)
» 有符號右移 shr(bits)
»> 無符號右移 ushr(bits)
& 與 and(bits)
| 或 or(bits)
^ 異或 xor(bits)
! 非 inv()

 

操作符重載與屬性委托、中綴調用

我們在使用委托屬性時也用過 operator 修飾符:

class Delegate {  operator fun getValue(thisRef: Any?, property: KProperty<*>): String {    //...  }  operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {    //...  }}

符合這樣方法簽名的 getValue 、 setValue 也是操作符函數,用于委托屬性的 getter 和 setter。

可以看出,操作符重載并不是一定要用如 * 、 + 、 < 這樣的符號來表示的,比如之前的 in 操作符,這里的 getter 、 setter。

除了以上這些標準的可被重載的操作符外,我們也可以通過中綴函數的調用來模擬自定義中綴操作符,實現形如 a in list 這樣的語法。

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對VeVb武林網的支持。


注:相關教程知識閱讀請移步到kotlin教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
精品欧美aⅴ在线网站| 亚洲精品一区二区网址| 日本欧美一级片| 欧洲成人免费视频| 久久精品国产综合| 亚洲欧美国产日韩天堂区| 欧美黑人巨大xxx极品| 高清日韩电视剧大全免费播放在线观看| 日韩的一区二区| 欧美日韩国产综合视频在线观看中文| 日韩毛片中文字幕| 欧美肥臀大乳一区二区免费视频| 国产成人精品午夜| 日韩视频精品在线| 亚洲国产精品高清久久久| 一本色道久久88综合亚洲精品ⅰ| 午夜精品理论片| 亚洲综合在线播放| 国产精品久久精品| 亚洲欧美成人精品| 日韩中文综合网| 在线观看精品自拍私拍| 亚洲自拍偷拍第一页| 国产日本欧美一区二区三区在线| 精品久久久在线观看| 国产成人精品久久二区二区| 日韩av免费看网站| 日韩av在线影院| 亚洲精品久久久一区二区三区| 在线看国产精品| 国产精品成人一区二区三区吃奶| 国产精品成人aaaaa网站| 久久精品中文字幕免费mv| 日韩中文有码在线视频| 欧美激情手机在线视频| 日韩电视剧在线观看免费网站| 欧美性猛交xxxx免费看| 日韩av手机在线观看| 日韩欧美在线视频| 成人免费xxxxx在线观看| 国产精品嫩草影院一区二区| 久久99久久久久久久噜噜| 国产精品视频1区| 精品国偷自产在线视频99| 26uuu日韩精品一区二区| 久久九九精品99国产精品| 久久久精品中文字幕| 国产91久久婷婷一区二区| 欧美黑人极品猛少妇色xxxxx| 欧美一区二粉嫩精品国产一线天| 国产精品入口日韩视频大尺度| 亚洲色图50p| 91久久久亚洲精品| 美女性感视频久久久| 国产成人精品一区二区三区| 亚洲欧美制服综合另类| 欧美激情xxxx| 日韩一区二区三区xxxx| 日韩美女视频在线观看| 日韩欧美精品中文字幕| 亚洲天堂网站在线观看视频| 国产有码在线一区二区视频| 亚洲free性xxxx护士hd| 精品动漫一区二区三区| 国产精品久久91| www.欧美三级电影.com| 欧美成人三级视频网站| 国产精品海角社区在线观看| 国产精品精品久久久| 国产精品大陆在线观看| 最新国产精品亚洲| 日韩视频永久免费观看| 九九热99久久久国产盗摄| 欧美一级视频免费在线观看| 日韩精品中文字幕在线播放| 欧美另类极品videosbest最新版本| 国产一区二区三区免费视频| 亚洲国产成人在线视频| 2019日本中文字幕| 日韩欧美亚洲国产一区| 在线中文字幕日韩| 中文字幕日韩欧美| 97国产精品视频| 欧美精品久久久久| 深夜福利亚洲导航| 精品国产一区二区在线| 亚洲人成五月天| 亚洲综合在线中文字幕| 久久色免费在线视频| 蜜臀久久99精品久久久无需会员| 亚洲欧美资源在线| 亚洲欧美日韩视频一区| 亚洲尤物视频网| 亚洲第一偷拍网| 欧美国产日韩一区二区| 欧美精品做受xxx性少妇| 国产欧美日韩高清| 亚洲欧美www| 国产欧美精品日韩精品| 亚洲无亚洲人成网站77777| 91中文字幕一区| 91久久国产精品91久久性色| 成人黄色av网站| 欧美成人免费va影院高清| 国产精品一区二区三区久久| 国产精品福利久久久| 欧美电影在线免费观看网站| 青青在线视频一区二区三区| 国产精选久久久久久| 亚洲精品久久久久中文字幕二区| 国产精品av在线播放| 夜夜躁日日躁狠狠久久88av| 国产精品aaa| 黑人巨大精品欧美一区免费视频| 亚洲精品久久久久国产| 欧美性少妇18aaaa视频| 欧美日韩国产综合视频在线观看中文| 日韩视频免费在线| 成人激情视频小说免费下载| 亚洲激情视频网| 国产精品丝袜高跟| 久久久999精品| 日韩亚洲综合在线| 国产欧美一区二区三区视频| 91精品在线观看视频| 国产一区二区三区视频免费| 日本人成精品视频在线| 91久久久久久久久久久久久| 美女视频久久黄| 主播福利视频一区| 国产欧美一区二区三区四区| 国产精品精品一区二区三区午夜版| 国产一区二区三区18| 国产精品成人av在线| 麻豆国产精品va在线观看不卡| 美女久久久久久久久久久| 国产一区二区三区中文| 国产精品激情自拍| 精品毛片三在线观看| 中文字幕亚洲欧美一区二区三区| 最近2019年中文视频免费在线观看| 91欧美精品成人综合在线观看| 欧美一级在线亚洲天堂| 欧美日韩激情视频8区| 爽爽爽爽爽爽爽成人免费观看| 欧美成人一区二区三区电影| 日日狠狠久久偷偷四色综合免费| 91精品国产高清久久久久久91| 亚洲精品资源美女情侣酒店| 日韩视频第一页| 日韩在线视频观看正片免费网站| 久久成人免费视频| 亚洲性线免费观看视频成熟| 精品中文字幕在线2019| 日韩欧美精品中文字幕| 欧美性xxxxx极品娇小| 免费97视频在线精品国自产拍| 久久中文精品视频| 亚洲欧美国产日韩天堂区| 久久99热精品这里久久精品| 久久久久久久久久久久av| 91色视频在线观看| 中文字幕自拍vr一区二区三区| 成人免费看吃奶视频网站|