日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Kotlin重载操作符和约定声明规则

發布時間:2023/12/20 编程问答 50 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Kotlin重载操作符和约定声明规则 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一.重載算數運算符

? Kotlin中最簡單明了的使用約定的例子就是算數運算符。在Java中算數運算符只可以使用在基本數據類型上,+號可以使用在String上。當我們想在BigInteger類上使用+,或者想使用+=添加元素到一個集合中時Java就做不到了。但是Kotlin中就可以做到。

1.重載二元算數運算符

? 首先從+開始,實現將兩個點的坐標值加起來,使用operator修飾符定義一個操作符函數

data class Point(val x: Int, val y: Int) {operator fun plus(other: Point): Point {return Point(x + other.x, y + other.y)} }

定義Point對象,此時可以使用+號

val point = Point(10, 20) val point2 = Point(20, 30) println(point + point2) >>Point(x=30, y=50)

? 也可以把操作符函數定義成擴展函數,而使用擴展函數語法也會是一種通用的定義操作符擴展函數的模板

operator fun Point.plus(other: Point): Point {return Point(x + other.x, y + other.y) }

? Kotlin不允許你自己定義操作符,下面是可以重載的操作符及函數名。為自己寫的Kotlin類定義的算數運算符其優先級和數字運算符是相同的。

?當我們定義操作符時,他接收的兩個操作數可以是不同類型的,操作符函數的返回值也可以是不同類型的。但是需要注意的是Kotlin操作符不支持左右兩個操作數交換順序

2.重載復合賦值操作符

? 正常情況下,當定義了一個操作符比如plus時,Kotlin會同時支持+和+=操作符。+=和-=叫做復合賦值操作符

//復合賦值運算符 var point3 = Point(1, 2) point3 += Point(2, 4) println(point3) >> Point(x=3, y=6)

+=運算符重載會定義一個叫plusAssign的函數,他沒有返回值,因為調用者會執行自增邏輯:

public inline operator fun <T> MutableCollection<in T>.plusAssign(element: T) {this.add(element) }

minusAssign,timesAssign也是類似的,Kotlin標準庫為可變集合定義了plusAssign這一類的函數。

?所以當你在代碼中使用+=時,理論上plus和plusAssign都會被調用。我們應該避免同時為+=添加plus和plusAssign操作符。如果調用者是不可變的(用val修飾),你應該只提供plus操作符返回一個全新的變量,如果調用者是可變的(var修飾),你應該只需要提供plusAssign操作符直接去修改調用者的值。另外在集合操作中,+和-不管是否是可變集合都會返回一個新的集合;+=和-=用在可變集合時會改變他們的值,使用在只讀集合時,會返回一個修改了的拷貝集合。(這意味著只有當可讀集合的引用是var才可以使用+=和-=)

3.重載一元操作符

? 一元運算符定義的方法和前面看到的是相同的,重載一元操作符的函數不需要任何參數

下表是所有可以重載的一元運算符

表達式函數名
+ aunaryPlus
- aunaryMinus
! anot
++ a,a ++inc
-- a,a --dec

+a直接返回a的值;-a直接返回a的負數值;!a如果a是Boolean類型的話就直接返回a的相反布爾值

二.重載比較運算符

? 正如算數運算符一樣,Kotlin中允許你將比較運算符(==,!=,>,<等等)用在任何對象上,而不僅僅是基本數據類型

1.相等運算符:equals

? ==操作符在Kotlin中會轉換為equals()函數的調用,!=也是對equals()函數的調用,只是結果相反。另外,相等性操作符的操作數是可空的,因為要比較null和相等性。a == b會先比較a是否為null,再調用a.equals(b)

? equals函數被標記為override,不像其他操作符的約定,不需要加operator標識符,因為他是實現在Any類中的,相等性比價對于任何Kotlin類都是適用的

2.排序運算符:compareTo

? 在Java類中,類進行查找最大值或者排序時,需要實現Comparable接口。而且進行比較時,沒有簡短的語法需要顯式的調用element1.compareTo(element2)進行比較。

? Kotlin也支持Comparable接口,但是接口中的compareTo方法可以通過約定調用:使用<,>,<=,和>=時會轉化為調用compareTo方法。compareTo的返回值為Int,表達式p1<p2等價于p1.compareTo(p2) < 0,其他比較符也是相同的。Comparable和equals一樣,也不需要operator操作符

//實現Comparable接口,Person對象在Kotlin和Java中都能用來比較排序等操作 //這里先比較Person的firstName,如果firstName相同再比較lastName data class Person(val firstName: String, val lastName: String ) : Comparable<Person> {override fun compareTo(other: Person): Int {return compareValuesBy(this, other, Person::firstName, Person::lastName)} } val person = Person("Li", "m1Ku") val person2 = Person("wang", "rick") println(person < person2) >> true

compareValuesBy函數可以讓你簡單方便的實現compareTo方法,這個函數接收要被比價計算值的回調。這個函數會調用每一個回調,并且比較值。如果值不同,那么返回比較結果,如果相同就調用下一個回調或者如果沒有更多回調時會返回0。回調可以是lambda表達式或者是屬性引用

三.集合和序列的約定

? 通過索引獲取元素或者為集合元素賦值,還有檢查一個元素是否屬于一個集合都是最常見的集合操作。這些操作都可以使用操作符語句,并且也可以為自己的類定義這些操作符。

1.通過索引獲取元素:get和set

? map元素的取值和賦值都可以通過[]中括號操作符完成

val params = hashMapOf("name" to "m1ku", "password" to "123456", "token" to "erwer3fg") val name = params["name"] params["password"] = "654321" println(name) println(params) >> m1Ku{name=m1ku, password=654321, token=erwer3fg}

? Kotlin中,索引操作符一個約定。使用索引操作符獲取一個元素會轉換為調用get方法,微元素設置會轉化為調用set方法。Map和MutableMap中已經定義了這樣的方法。

? 如何在自己的類中定義這樣的操作符呢?

? 我們需要做的就是定義一個由operator修飾的名字叫get的函數

//定義所以操作符函數,獲取Point的x和y坐標 operator fun Point.get(index: Int): Int {return when (index) {0 -> x1 -> yelse ->throw IndexOutOfBoundsException()} } val point = Point(10, 88) //調用這個時,轉化為調用get函數 println(point[1]) >> 88

? 定義一個set函數能讓我們已類似的方式為集合元素賦值

operator fun Point.set(index: Int, value: Int) {when (index) {0 -> x = value1 -> y = valueelse ->throw IndexOutOfBoundsException()} } val point = Point(10, 88) //使用約定語句為元素賦值 point[0] = 100 println(point[0]) >> 100

set函數最后一個元素是賦值運算式右邊的值,其他元素是方括號中給定的索引

2."in"約定

? in操作符:判斷一個對象是否屬于一個集合,對應調用的函數是contains

operator fun Rectangle.contains(p: Point): Boolean {return p.x in upperLeft.x until lowerRight.x &&p.y in upperLeft.y until lowerRight.y } val rect = Rectangle(Point(10, 20), Point(50, 50)) println(Point(20, 30) in rect) >> true

in右邊是調用contains函數的對象,左邊是傳遞給函數的參數

val point = Point(20,30) //下面這兩句是等價的 point in rect rect.contains(point)

3.rangeTo約定

? 使用..語句創建一個序列,其實..操作符是一種簡單的調用rangeTo函數的方式。可以為自己的類定義一個rangTo函數,但是當實現了comparable接口的類不需要自己自己定義這個函數。Kotlin標準庫為實現了comparable接口的類定義了rangeTo方法。

//Circle實現了comparable接口,可以調用rangeTo函數返回一個序列 //我們可以判斷不同元素是否在序列中 val startC = Circle(10f) val endC = Circle(200f) val circle = Circle(5f) val circleRange = startC..endC println(circle in circleRange) >> false

4.for循環的"iterator"約定

? Kotlin的for循環和范圍檢查使用的都是in操作符,但是在這里的意義是不同的,這里用來執行迭代操作。在Kotlin中這也是一種約定,這意味著iterator方法可以定義為擴展函數。這就是為什么一個普通Java的String也可以進行迭代了:在String的超類CharSequence上定義了iterator擴展函數

? 我們可以為自己的類定義iterator方法

operator fun ClosedRange<Circle>.iterator(): Iterator<Circle> =object : Iterator<Circle> {var current = startoverride fun hasNext(): Boolean {return current <= endInclusive}override fun next(): Circle {return current}}

四.解構聲明和組件函數

? 現在已經熟悉了約定的使用,現在看一下數據類的最后一個特點,解構聲明。這個特性可以將一個復合值拆開并將其存儲在不同的變量中。

val p = Point(10,20) //聲明x,y變量,并用p給他們初始化賦值 val(x,y) = p println(x) >> 10

? 解構聲明看起來和普通的變量聲明很像,但是解構聲明是將一組變量放在括號中。這里解構聲明也是用到了約定。對于解構聲明中的每一個變量,都會調用一個叫componentN的函數,N是變量聲明的位置。

//上面的解構聲明等價于下面兩行代碼 x = p.component1() y = p.component2()

? 對于數據類,編譯器為主構造器中聲明的每個屬性生成了一個componentN函數

? 對于有多個返回值的函數,使用解構聲明是很方便的,我們可以將需要返回的值定義在一個類中,然后函數返回這個類,再使用解構聲明就方便的獲取到了需要的值

data class NameComponent(val name: String, val extension: String) fun splitName(fullName: String): NameComponent {val result = fullName.split(".")return NameComponent(result[0], result[1]) }val (name, extension) = splitName("kotlin實戰.pdf") println("name = $name extension = $extension") >> name = kotlin實戰 extension = pdf

? Kotlin為集合和數組定義了componentN函數,所以集合可以直接使用解構聲明。當集合大小已知時,可以簡化為

fun splitName2(fullName: String): NameComponent {val (name, extension) = fullName.split(".",limit = 2)return NameComponent(name, extension) }

? Kotlin標準函數庫允許我們通過解構聲明獲得容器中的前5個元素

1.解構聲明和循環

? 解構聲明不止可以用在函數的頂層語句中,而且還可以用在其他可以聲明變量的地方,比如:循環。

//遍歷一個map //這個例子使用了兩次約定:迭代對象,解構聲明 fun printEntry(map: Map<String, String>) {for ((key, value) in map) {println("$key$value")} }

五.重用屬性訪問邏輯:委托屬性

? 委托屬性依賴于約定,它是Kotlin一個獨特的強有力的特性。這個特性實現的基礎是委托:委托是一種設計模式,它可以將一個對象要執行的任務,委托給另一個對象執行。輔助執行的對象叫:委托。當把這種模式使用在屬性上時,就可以把訪問器的邏輯委托給一個輔助對象。

1.委托屬性的基本操作

? 屬性委托的語法如下:

class Example {var p: String by Delegate() }

? 這里屬性p將它的訪問器邏輯委托給Delegate類的一個對象,通過關鍵字by對其后的表達式求值來獲取這個對象。根據約定,委托類必須有getValue和setValue方法。像往常一樣,他們可以是成員函數也可以是擴展函數。

? 可以把example.p當做普通屬性使用,但它將調用Delegate類輔助屬性的方法

//調用委托類的setValue方法 example.p = "hhahah" //調用委托類的getValue方法 val value = example.p

2.使用委托屬性:惰性初始化和 by lazy()

? 惰性初始化,是一種常見的模式,直到第一次訪問某個屬性時,對象的一部分才會按需創建。當初始化過程占據很多的資源,并且當對象使用時這些數據并不會用到時,這種模式是很有用的。

class Person(val name: String) {//使用lazy標準庫函數實現委托val emails by lazy { loadEmails(this) }private fun loadEmails(person: Person): List<String> {println("初始化函數調用")return listOf("1", "2")} } val p = Person("m1Ku") //當第一次使用這個屬性時,屬性才會初始化即惰性初始化 p.emails >> 初始化函數調用

? lazy函數返回一個包含適當簽名的getValue的方法的對象,所以就可以和by關鍵字一起使用創建一個委托屬性。lazy函數的參數一個lambda,執行初始化值的邏輯。lazy函數默認是線程安全的。

3.實現委托屬性

class User {var age: Int by Delegates.observable(18,{ property, oldValue, newValue ->println("${property.name} $oldValue $newValue")}) } val u1 = User() u1.age = 10 >>age 18 10

Delegates.observable()包含兩個參數:初始值和修改處理Handler,每次修改屬性值都會調用Handler。

? by函數右邊不一定是創建實例。它可以是函數調用,另一個屬性,或者其他表達式,只要這個表達式的值是一個對象:編譯器可以以正確的類型參數調用getValue和setValue方法。

4.委托屬性的轉換規則

? 總結一下委托屬性的規則,假設有下面這個有委托屬性的類:

class Foo { var c: Type by MyDelegate() }

? MyDelegate的實例會被保存在一個隱藏屬性中,我們用<delegate>代表他。編譯器會用一個KProperty類型的對象便是屬性,我們且用<property>代表他。編譯器生成如下的代碼:

class Foo {private val <delegate> = MyDelegate()var c: Typeset(value: Type) = <delegate>.setValue(c, <property>, value)get() = <delegate>.getValue(c, <property>) }

因此每次獲取屬性時,其對應的setValue和getValue方法就會調用

5.在map中存儲屬性值

? 另一個委托屬性能派上用場的地方是:用在一個動態定義屬性集的對象上。這樣的對象叫做:自訂對象(expando objects )。

class Fruit() {val attributes = hashMapOf<String, String>()fun setAttribute(attrName: String, value: String) {attributes[attrName] = value}//將map作為委托屬性val name: String by attributes }

? 可以直接在map后面使用by關鍵字,這是因為標準庫為Map和MutableMap接口定義了getValue和setValue的擴展函數,屬性的名字自動用在map中的鍵,屬性的值就是其對應的map中的值

總結

以上是生活随笔為你收集整理的Kotlin重载操作符和约定声明规则的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。