欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Kotlin學(xué)習(xí)教程之操作符重載詳解

 更新時(shí)間:2018年02月03日 10:08:59   作者:Ebn  
這篇文章主要給大家介紹了關(guān)于Kotlin學(xué)習(xí)教程之操作符重載的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。

前言

在 Kotlin 中,我們可以用 約定的操作符,代替 調(diào)用代碼中以特定的命名定義的函數(shù),來(lái)實(shí)現(xiàn) 與之對(duì)應(yīng)的操作。例如在類中定義了一個(gè)名為 plus 的特殊方法,就可以使用加法運(yùn)算符 + 代替 plus() 的方法調(diào)用。由于你無(wú)法修改已有的接口定義,因此一般可以通過(guò) 擴(kuò)展函數(shù) 來(lái)為現(xiàn)有的類增添新的 約定方法,從而使得 操作符重載 這一語(yǔ)法糖適應(yīng)任何現(xiàn)有的 Java 類。

算術(shù)運(yùn)算符

我們就從最簡(jiǎn)單直接的例子 + 這一類算術(shù)運(yùn)算符開(kāi)始。

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 只是一個(gè)普通方法,不能通過(guò) + 調(diào)用。
  • 操作符是有優(yōu)先級(jí)的,比較 * 優(yōu)先級(jí)高于 +,不論這個(gè)操作符應(yīng)用于什么對(duì)象,這種優(yōu)先級(jí)都是固定存在的。
  • plus 方法的參數(shù)類型是任意的,因此可以方法重載,但是 參數(shù)數(shù)量只能是 1 ,因?yàn)?+ 是一個(gè)二元操作符。plus 方法的返回值類型也是任意的。
  • 如果出現(xiàn)多個(gè)方法簽名相同的 operator 擴(kuò)展方法,根據(jù) import 決定使用哪個(gè)一,例如:
// 第一個(gè)文件:
package package0
operator fun Point.times(value: Int) = Point(x * value, y * value)
// 第二個(gè)文件:
package package1
operator fun Point.times(value: Int) = Unit // Do nothing.
// 使用第一個(gè)擴(kuò)展操作符:
import package0.times
val newPoint = Point(1, 2) * 3

Kotlin 為一些基本類型預(yù)定義了一些操作符方法,我們平時(shí)常寫(xiě)的基本數(shù)據(jù)計(jì)算也可以翻譯成調(diào)用這些操作符方法,比如 (2 + 3) * 4 可以翻譯成 2.plus(3).times(4),2 + 3 * 4 可以翻譯成 2.plus(3.times(4))。根據(jù)擴(kuò)展函數(shù)的語(yǔ)法,擴(kuò)展函數(shù)無(wú)法覆蓋與類已有的方法簽名相同的方法,因此,不必?fù)?dān)心隨隨便便給 Int 自定義一個(gè) plus 擴(kuò)展方法就能讓 1 + 1 變得不等于 2。

同時(shí),所有操作符都針對(duì)基本類型做了優(yōu)化,比如 1 + 2 * 3、4 < 5,不會(huì)為它們引入函數(shù)調(diào)用的開(kāi)銷。

所有可重載的算術(shù)運(yùn)算符有:

表達(dá)式  翻譯為
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)先級(jí)與普通的數(shù)字類型運(yùn)算符優(yōu)先級(jí)相同。其中 rangeTo 會(huì)在下面說(shuō)明。

廣義賦值操作符

表達(dá)式  翻譯為
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 中被棄用)

對(duì)于以上廣義賦值操作符:

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

我們使用這個(gè)例子來(lái)理解:為什么使用 var 定義的 s1 會(huì)導(dǎo)致 += 報(bào)錯(cuò)呢?因?yàn)槔碚撋?,?zhí)行 += 時(shí),既可以調(diào)用 s1 = s1 + s2,也就是 s1 = s1.plus(s2),又可以調(diào)用 s1.plusAssign(s2),都符合操作符重載約定,這樣就會(huì)產(chǎn)生歧義,而如果使用 val 定義 s1,則只可能執(zhí)行 s1.plusAssign(s2),因?yàn)?s1 不可被重新賦值,因此 s1 = s1 + s2 這樣的語(yǔ)法是出錯(cuò)的,永遠(yuǎn)不能能調(diào)用,那么調(diào)用 s1 += s2 就不會(huì)產(chǎn)生歧義了。

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

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

Kotlin 標(biāo)準(zhǔn)庫(kù)中就是這么實(shí)現(xiàn)的:

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

in

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

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

在 for 循環(huán)中使用 in 操作符會(huì)執(zhí)行迭代操作,for(x in list) { /* 遍歷 */ } 將被轉(zhuǎn)換成 list.iterator() 的調(diào)用,然后在上面重復(fù)調(diào)用hasNext 和 next 方法。

rangeTo

rangeTo 用于創(chuàng)建一個(gè)區(qū)間。例如 1..10 也就是 1.rangeTo(10) 代表了從 1 到 10 這 10 個(gè)數(shù)字,Int.rangeTo 方法返回一個(gè) IntRange 對(duì)象,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 實(shí)現(xiàn)了 Iterable 接口,因此 1..10 可以用來(lái)迭代:

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

IntRange 還實(shí)現(xiàn)了接口 ClosedRange ,可以用來(lái)判斷某元素是否屬于該區(qū)間。

Kotlin 為 Comparable 定義了擴(kuò)展函數(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 對(duì)象都可以使用 .. 區(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 天后.
 
 // 判斷某日期是否在某兩個(gè)日期范圍內(nèi).
 println(c3 in c1..c2)
 println(c4 in c1..c2)
}
/*
true
false
*/

一元前綴操作符

表達(dá)式  翻譯為
+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)
*/

遞增與遞減

表達(dá)式  翻譯為
a++ a.inc()
a– a.dec()

編譯器自動(dòng)支持與普通數(shù)字類型的前綴、后綴自增運(yùn)算符相同的語(yǔ)義。例如后綴運(yùn)算會(huì)先返回變量的值,然后才執(zhí)行 ++ 操作。

索引訪問(wèn)操作符

表達(dá)式  翻譯為
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 // 寫(xiě) sp.
}

調(diào)用操作符

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

相等與不等操作符

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

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

比較操作符

表達(dá)式  翻譯為
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 也用作比較會(huì)報(bào)錯(cuò), 因?yàn)?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("移動(dòng)迷宮", 7, df.parse("2018-01-02"))
 println(movie0 < movie1)
 println(movie1 < movie2)
}
/*
false
true
*/

其中的 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
}

我們定義一個(gè) Movie 類,它實(shí)現(xiàn)了 Comparable 接口,在比較時(shí),希望按照 評(píng)分 、 上映日期 、 電影名稱 的優(yōu)先級(jí)順序排序。可以簡(jiǎn)單的使用比較操作符對(duì) Movie 對(duì)象進(jìn)行“大小比較”。

操作符函數(shù)與 Java

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

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

Java 中的位運(yùn)算符在 Kotlin 中是沒(méi)有的 ,它們只能使用普通方法加中綴表達(dá)式使用,只能用于 Int 和 Long,對(duì)應(yīng)關(guān)系如下:

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

操作符重載與屬性委托、中綴調(diào)用

我們?cè)谑褂梦袑傩詴r(shí)也用過(guò) operator 修飾符:

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

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

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

除了以上這些標(biāo)準(zhǔn)的可被重載的操作符外,我們也可以通過(guò)中綴函數(shù)的調(diào)用來(lái)模擬自定義中綴操作符,實(shí)現(xiàn)形如 a in list 這樣的語(yǔ)法。

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。

相關(guān)文章

  • Android仿新浪微博/QQ空間滑動(dòng)自動(dòng)播放視頻功能

    Android仿新浪微博/QQ空間滑動(dòng)自動(dòng)播放視頻功能

    相信用過(guò)新浪微博或者QQ空間的朋友都看到過(guò)滑動(dòng)自動(dòng)播放視頻的效果,那么這篇文章跟大家分享下如何利用Android實(shí)現(xiàn)這一個(gè)功能,有需要的朋友們可以參考借鑒。
    2016-09-09
  • Android編程之繪圖canvas基本用法示例

    Android編程之繪圖canvas基本用法示例

    這篇文章主要介紹了Android編程之繪圖canvas基本用法,結(jié)合實(shí)例形式分析了Android canvas繪圖的相關(guān)方法與使用技巧,需要的朋友可以參考下
    2017-09-09
  • Android System fastboot 介紹和使用教程

    Android System fastboot 介紹和使用教程

    Fastboot是Android快速升級(jí)的一種方法,Fastboot的協(xié)議fastboot_protocol.txt在源碼目錄./bootable/bootloader/legacy下可以找到,本文給大家介紹Android System fastboot 介紹和使用教程,感興趣的朋友一起看看吧
    2024-01-01
  • Android解讀Native崩潰棧信息的方法詳解

    Android解讀Native崩潰棧信息的方法詳解

    大部分的 Android 開(kāi)發(fā)者使用的主要語(yǔ)言都是 Kotlin / Java,他們的崩潰棧信息非常清晰,也非常好定位到問(wèn)題,本文小編給大家介紹了Android如何解讀Native崩潰棧信息,需要的朋友可以參考下
    2023-11-11
  • Android控件實(shí)現(xiàn)水滴效果

    Android控件實(shí)現(xiàn)水滴效果

    這篇文章主要為大家詳細(xì)介紹了Android控件實(shí)現(xiàn)水滴效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-02-02
  • 基于linux與windows平臺(tái)下 如何下載android sdk源代碼的方法詳解

    基于linux與windows平臺(tái)下 如何下載android sdk源代碼的方法詳解

    本文主要是介紹在linux和windows平臺(tái)下,如何下載android sdk的源代碼,注意是sdk的源代碼,而不是android的所有源代碼,同時(shí)介紹如何把sdk源代碼加入到eclipse里,使android 平臺(tái)手機(jī)開(kāi)發(fā)者可以直接查看源代碼,通過(guò)閱讀SDK源碼,能更好的理解和運(yùn)用Android的API
    2013-05-05
  • Android Studio給各種控件加邊框的操作方法

    Android Studio給各種控件加邊框的操作方法

    這篇文章主要介紹了Android Studio給各種控件加邊框的操作方法,本文通過(guò)圖文實(shí)例相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-12-12
  • Android仿新浪微博分頁(yè)管理界面(3)

    Android仿新浪微博分頁(yè)管理界面(3)

    這篇文章主要為大家詳細(xì)介紹了Android仿新浪微博分頁(yè)管理界面,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-11-11
  • Android NDK中socket的用法以及注意事項(xiàng)分析

    Android NDK中socket的用法以及注意事項(xiàng)分析

    本篇文章是對(duì)Android NDK中socket的用法以及注意事項(xiàng)進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-06-06
  • Android SQLite詳解及示例代碼

    Android SQLite詳解及示例代碼

    本文主要講解Android SQLite,這里對(duì)數(shù)據(jù)庫(kù)SQLite 做了詳細(xì)的知識(shí)資料整理,并附示例代碼和實(shí)現(xiàn)效果圖,有需要的小伙伴可以參考下
    2016-08-08

最新評(píng)論